Scrapy框架的使用之Scrapy对接硒

Scrapy抓取页面的方式和请求库相似,都是直接模拟HTTP请求,而Scrapy也不能抓取的JavaScript动态渲染的页面。在前文中抓取的JavaScript渲染的页面有两种方式。一种是分析的Ajax请求,找到其对应的接口抓取,Scrapy一样能够用此种方式抓取。另外一种是直接用硒或飞溅模拟浏览器进行抓取,咱们不须要关心页面后台发生的请求,也不须要分析渲染过程,只须要关心页面最终结果便可,可见便可爬。那么,若是Scrapy能够对接硒,那Scrapy就能够处理任何网站的抓取了。

一,本节目标

本节咱们来看看Scrapy框架如何对接硒,以PhantomJS进行演示。咱们依然抓取淘宝商品信息,抓取逻辑和前文中用硒抓取淘宝商品彻底相同。

二,准备工做

请确保PhantomJS和MongoDB中已经安装好并能够正常运行,安装好Scrapy,硒,PyMongo库。

三,新建项目

首先新建项目,名为scrapyseleniumtest,命令以下所示:

scrapy startproject scrapyseleniumtest复制代码

新建一个蜘蛛,命令以下所示:

scrapy genspider淘宝网www.taobao.com复制代码

修改
ROBOTSTXT_OBEY
False
,以下所示:

ROBOTSTXT_OBEY = False复制代码

四,定义

定义首先
Item
对象,名为
ProductItem
,代码以下所示:

来自 scrapy import Item,Field 

class  ProductItem (Item):

     collection = 'products'image
     = Field()
    price = Field()
    deal = Field()
    title = Field()
    shop = Field()
    location = Field()复制代码

这里咱们定义了6个领域,也就是6个字段,跟以前的案例彻底相同。定义而后一个了
collection
属性,即此项目保存的MongoDB中的集合名称。

初步实现蜘蛛的
start_requests()
方法,以下所示:

从 scrapy 进口请求,蜘蛛
从的urllib.parse 进口报价
从 scrapyseleniumtest.items 导入 ProductItem 

类 TaobaoSpider (蜘蛛):
     名称= '淘'
     allowed_domains = [ 'www.taobao.com' ] 
    BASE_URL = “https://s.taobao的.com /查询q =”?

    DEF  start_requests (个体):
        为关键字在 self.settings.get('关键字'):
            对页面在范围(1,self.settings.get('MAX_PAGE')+ 1):
                url = self.base_url + quote(关键字)
                yield请求(url = url,callback = self.parse,meta = { 'page':page},dont_filter = True)复制代码

首先定义了一个
base_url
,即商品列表的URL,其后拼接一个搜索关键字就是该关键字在淘宝的搜索结果商品列表页面

用关键字
KEYWORDS
标识,定义为一个列表最大翻页页码用。
MAX_PAGE
表示它们统必定义在setttings.py里面,以下所示:

KEYWORDS = [ 'iPad' ] 
MAX_PAGE = 100复制代码

start_requests()
方法里,咱们首先遍历了关键字,遍历了分页页码,构造并生成请求。因为每次搜索的URL是相同的,因此页码分页用
meta
参数来传递,设置同时
dont_filter
不去重。这样爬虫启动的时候,就会生成每一个关键字对应的商品列表的每一页的请求了

五,对接Selenium

接下来咱们须要处理这些请求的抓取。此次咱们对接Selenium进行抓取,采用Downloader Middleware来实现。在Middleware里面的
process_request()
方法里对每一个抓取请求进行处理,启动浏览器并进行页面渲染,再将渲染后的结果构造一个
HtmlResponse
。对象返回代码实现以下所示:

来自 selenium import webdriver 
from selenium.common.exceptions 导入 TimeoutException异常
从 selenium.webdriver.common.by 进口经过
从 selenium.webdriver.support.ui 进口 WebDriverWait 
从 selenium.webdriver.support 进口 expected_conditions 为 EC 
从 scrapy.http 进口 HtmlResponse 
从记录import getLogger 

类 SeleniumMiddleware ():
    def  __init__(个体,超时=无,service_args = []) :
         self.logger = getLogger(__ name__)
        self.timeout =超时
        self.browser = webdriver.PhantomJS(service_args = service_args)
        self.browser.set_window_size( 1400, 700)
        的自我。 browser.set_page_load_timeout(self.timeout)
        self.wait = WebDriverWait(self.browser,self.timeout)

     def  __del __ (self):
         self.browser.close()

     def  process_request (self,request,spider):
        “”“ 
        用PhantomJS抓取页面
        :param request:请求对象
        :param spider:Spider对象
        :return:HtmlResponse 
        “”“
         self.logger.debug('PhantomJS is Starting')
        page = request.meta.get('page',1)
        try:
            self.browser.get(request.url)
            if page> 1:
                input = self.wait.until(
                    EC.presence_of_element_located((By.CSS_SELECTOR,'#mainsrp-pager div.form> input))) submit = self.wait.until( EC.element_to_be_clickable((By.CSS_SELECTOR,'# mainsrp-pager div.form> span.btn.J_Submit'))) input.clear() input.send_keys(page) submit.click() self.wait.until(EC.text_to_be_present_in_element((By.CSS_SELECTOR,'#mainsrp-pager li.item.active> span'),str(page))) self.wait .until(EC.presence_of_element_located((By.CSS_SELECTOR,'.m-itemlist .items .item'))) return HtmlResponse(url = request.url,body = self.browser.page_source,request = request,encoding = 'utf -8',status = 200), 除了 TimeoutException: return HtmlResponse(url = request.url,status = 500,request = request) @classmethod def from_crawler (cls,crawler): return cls(timeout = crawler.settings.get(' SELENIUM_TIMEOUT '), service_args = crawler.settings.get('PHANTOMJS_SERVICE_ARGS'))复制代码

首先咱们在
__init__()
里对一些对象进行初始化,包括
PhantomJS
WebDriverWait
等对象,同时设置页面大小和页面加载超时时间
。在
process_request()
方法中,咱们经过请求的
meta
属性电子杂志当前须要爬取的页码,调用PhantomJS对象的
get()
方法访问请求的对应的URL。这就至关于从请求对象里获取请求连接,而后再用PhantomJS加载,而再也不使用Scrapy里的下载。

随后的处理等待和翻页的方法在此再也不赘述
,和前文的原理彻底相同。最后,页面加载完成以后,咱们调用PhantomJS的
page_source
属性便可电子杂志当前页面的源代码,而后用它来直接构造并一个报道查看
HtmlResponse
对象构造这个对象的时候须要传入多个参数,如。
url
body
等,这些参数实际上就是它的基础属性能够在官方文档查看
HtmlResponse
对象的结构:HTTPS://doc.scrapy.org/en /latest/topics/request-response.html。这样咱们就成功利用PhantomJS来代替Scrapy完成了页面的加载,最后将响应返回便可。

有人可能会纳闷:为何实现这么一个Downloader Middleware就能够了?以前的请求对象怎么办?Scrapy再也不处理了吗?回复后又又传递给了谁?

是的,申请对象到这里就不会再处理了,也不会再像之前同样交给下载器下载.Response会直接传给蜘蛛进行解析。

咱们须要回顾一下Downloader Middleware的
process_request()
方法的处理逻辑,内容以下所示:

process_request()
方法返回响应对象的时候,更低优先级的Downloader Middleware的
process_request()
process_exception()
方法就不会被继续调用了,转而开始执行每一个Downloader Middleware的
process_response()
方法,调用完毕以后直接响应对象发送给Spider来处理。

这里直接返回了一个
HtmlResponse
对象,它是Response的子类,返回以后便顺次调用每一个Downloader Middleware的
process_response()
方法。而在
process_response()
中咱们没有对其作特殊处理,它会被发送给给Spider,传给Request的回调函数进行解析。

到如今,咱们应该能了解Downloader Middleware实现Selenium对接的原理了。

在settings.py里,咱们设置调用刚才定义的
SeleniumMiddleware
,以下所示:

DOWNLOADER_MIDDLEWARES = { 
    'scrapyseleniumtest.middlewares.SeleniumMiddleware':543,
}复制代码

六,解析页面

响应对象就会回传给蜘蛛内的回调函数进行解析因此下一步咱们就实现其回调函数,对网页来进行解析,代码以下所示:

def  parse (self,response):
     products = response.xpath(
         '// div [@ id =“mainsrp-itemlist”] // div [@ class =“items”] [1] // div [contains(@class , “项目”)] ')
    为产品在产品:
        产品= ProductItem()
        项[ '价格' ] = '' 。加入(product.xpath(' .//div[contains(@class, “价格”)] ()。)(); // text()').extract())。strip()
        item [ 'title' ] = '' .join(product.xpath('.//div[contains ( @class,“title”)] // text()').extract())。strip()
        item [ 'shop' ] = ''. join(product.xpath('.div[contains(@class,“shop”)] // text()').extract())。strip()
        item [ 'image' ] = ''. join(product.xpath('。 / / div [@ class =“pic”] // img [contains(@class,“img”)] / @ data-src').extract())。strip()
        item [ 'deal' ] = product。 xpath(' .//div[contains( @class,“deal-cnt”)] // text()').extract_first()
        item [ 'location' ] = product.xpath('.// div [contains( @class,“location”)] // text()').extract_first()
        yield item复制代码

在这里咱们使用的XPath进行解析,调用
response
变量的
xpath()
方法便可。首先咱们传递选取全部商品对应的XPath中,能够匹配全部商品,随后对结果进行遍历,依次选取每一个商品的名称,价格,图片等内容,并构造一个报道查看
ProductItem
对象。

七,存储结果

最后咱们实现一个Item Pipeline,将结果保存到MongoDB,以下所示:

导入 pymongo 

类 MongoPipeline (object):
    def  __init__ (self,mongo_uri,mongo_db):
         self.mongo_uri = mongo_uri 
        self.mongo_db = mongo_db 

    @classmethod 
    def  from_crawler (cls,crawler):
        return cls(mongo_uri = crawler.settings.get(' MONGO_URI'),mongo_db = crawler.settings.get('MONGO_DB'))

    def  open_spider(个体,蜘蛛):
         self.client = pymongo.MongoClient(self.mongo_uri)
        self.db = self.client [self.mongo_db]

     DEF  process_item(self,item,spider):
         self.db [item.collection] .insert(dict(item))
         return item

     def  close_spider (self,spider):
         self.client.close()复制代码

此实现和前文中存储到MongoDB中的方法彻底一致,原理再也不赘述记得在settings.py中开启它的调用,以下所示:

ITEM_PIPELINES = { 
    'scrapyseleniumtest.pipelines.MongoPipeline':300,
}复制代码

其中,
MONGO_URI
状语从句:
MONGO_DB
的定义以下所示:

MONGO_URI = 'localhost'MONGO_DB
 = 'taobao'复制代码

八,运行

整个项目就完成了,执行以下命令启动抓取便可:

scrapy抓取淘宝复制代码

运行结果以下图所示。

查看MongoDB的,结果以下图所示。

这样咱们便成功在Scrapy中对接硒并实现了淘宝商品的抓取。

九,本节代码

本节代码地址为:HTTPS://github.com/Python3WebSpider/ScrapySeleniumTest。

十,结语

咱们经过实现了Downloader Middleware的方式实现了Selenium的对接。但这种方法实际上是阻塞式的,也就是说这样就破坏了了Scrapy异步处理的逻辑,速度会受到影响。为了避免破坏其异步加载逻辑,咱们能够使用飞溅实现。下一节咱们再来看看Scrapy对接飞溅的方式。


本资源首发于崔庆才的我的博客静觅: Python3网络爬虫开发实战教程 | 静觅html

如想了解更多爬虫资讯,请关注个人我的微信公众号:进击的Codergit

weixin.qq.com/r/5zsjOyvEZ… (二维码自动识别)github

相关文章
相关标签/搜索