Scrapy实战篇(六)之Scrapy配合Selenium爬取京东信息(上)

在之前的一篇实战之中,我们已经爬取过京东商城的文胸数据,但是前面的那一篇其实是有一个缺陷的,不知道你看出来没有,下面就来详细的说明和解决这个缺陷。

我们在京东搜索页面输入关键字进行搜索的时候,页面的返回过程是这样的,它首先会直接返回一个静态的页面,页面的商品信息大致是30个,之所以说是大致,因为有几个可能是广告商品,之后,当我们鼠标下滑的使用,京东后台使用Ajax技术加载另外的30个商品数据,我们看上去是60个数据,其实这60个数据是分两次加载出来的,而且只是在你鼠标下滑到一定的位置才会加载那另外的30个数据。

当你点击页面最后的第二页的时候,仔细观察新的url你会发现它的页面显示是第三页。
下面是初始第一面和点击第二页之后的url

# 第一页的url
https://search.jd.com/Search?keyword=%E6%89%8B%E6%9C%BA&enc=utf-8&qrst=1&rt=1&stop=1&vt=2&wq=%E6%89%8B%E6%9C%BA&cid2=653&cid3=655&page=1&s=1&click=0

# 点击第二页的url
https://search.jd.com/Search?keyword=%E6%89%8B%E6%9C%BA&enc=utf-8&qrst=1&rt=1&stop=1&vt=2&wq=%E6%89%8B%E6%9C%BA&cid2=653&cid3=655&page=3&s=59&click=0

page参数一个是1,一个是3。
因此可以得知,京东的第二页的信息是使用Ajax加载出来的,而不是直接请求url的形式。如果我们想要拿到另外的30个信息,一方面是需要js渲染,另一方面是实现滚动条下拉,触发Ajax请求。

知道了过程,下面就是着手解决这个问题,由于Scrapy框架只能加载静态数据,因此我们需要另外的工具来配合Scrapy实现爬取页面的完整信息。

我们的技术路线是这样的,使用selenium加Firefox来实现目的。
实现的过程是这样的,将selenium作为scrapy的下载中间件,执行js脚本实现滚动条的下拉,并且实现js的渲染。

下面就来演示。

spider.py文件

from scrapy import Spider,Request
from selenium import webdriver


class JingdongSpider(Spider):
    name = 'jingdong'

    def __init__(self):
        self.browser = webdriver.Firefox()
        self.browser.set_page_load_timeout(30)

    def closed(self,spider):
        print("spider closed")
        self.browser.close()

    def start_requests(self):
        start_urls = ['https://search.jd.com/Search?keyword=%E6%96%87%E8%83%B8&enc=utf-8&qrst=1&rt=1&stop=1&vt=2&suggest=1.his.0.0&page={}&s=1&click=0'.format(str(i)) for i in range(1,2,2)]
        for url in start_urls:
            yield Request(url=url, callback=self.parse)


    def parse(self, response):
        selector = response.xpath('//ul[@class="gl-warp clearfix"]/li')
        print(len(selector))
        print('---------------------------------------------------')

这里将webdriver定义在spider文件的好处是,不需要每一次请求url都打开和关闭浏览器。
其中的closed()方法,是在爬虫程序结束之后,自动关闭浏览器。
由于这里是演示之用,我们就以一个页面来测试,看一下最后的结果是不是返回60条数据,如果是60条左右就证明我们的selenium起作用了,如果仅仅是30条左右的数据,就证明失败。

middlewares.py

下面的这个文件是主要逻辑实现

from scrapy.http import HtmlResponse
from selenium.common.exceptions import TimeoutException
import time

class SeleniumMiddleware(object):
    def process_request(self, request, spider):
        if spider.name == 'jingdong':
            try:
                spider.browser.get(request.url)
                spider.browser.execute_script('window.scrollTo(0, document.body.scrollHeight)')
            except TimeoutException as e:
                print('超时')
                spider.browser.execute_script('window.stop()')
            time.sleep(2)
            return HtmlResponse(url=spider.browser.current_url, body=spider.browser.page_source,
                                encoding="utf-8", request=request)

在程序中,我们执行了js脚本,来实现滚动条下拉,触发Ajax请求,之后我们简短的等待,来加载Ajax。 在scrapy官方文档中,对下载中间件有着比较详细的说明,当某个下载中间件返回的是response对象的时候,之后的下载中间件将不会被继续执行,而是直接返回response对象。

之后,我们只需要在设置中将当前的下载中间件添加到settings.py文件中,就可以实现了。

运行程序,会发现自动打开了Firefox浏览器,并且实现了滚动条的下拉,关闭浏览器。控制台打印信息如下。

不妨多试验几个页面,会发现效果也是一样的。

posted @ 2017-09-21 18:39  cnkai  阅读(14440)  评论(0编辑  收藏  举报