Python网络爬虫实战之十四:Scrapy结合scrapy-splash爬取动态网页数据

0.155字数 1245阅读 1559

目录:Python网络爬虫实战系列

正文:

一、Scrapy爬取动态网页数据的原理

之前我们学习的内容都是抓取静态页面,每次请求,它的网页全部信息将会一次呈现出来。 但是,像比如一些购物网站,他们的商品信息都是js加载出来的,并且会有ajax异步加载。像这样的情况,直接使用scrapy的Request请求是拿不到我们想要的信息的,比如京东商城的商品列表中的价格、店铺名称、评论数量。解决的方法就是使用scrapy-splash。

scrapy-splash加载js数据是基于Splash来实现的,Splash是一个Javascript渲染服务。它是一个实现了HTTP API的轻量级浏览器,Splash是用Python实现的,同时使用Twisted和QT,而我们使用scrapy-splash最终拿到的response相当于是在浏览器全部渲染完成以后,拿到的渲染之后的网页源代码。

二、爬取之前的环境部署

1、安装docker

在windows环境下,安装docker简便的方法是使用docker toolbox,由于Docker引擎的守护进程使用的是Linux的内核,所以我们不能够直接在windows中运行docker引擎。而是需要在你的机器上创建和获得一个Linux虚拟机,用这个虚拟机才可以在你的windows系统上运行Docker引擎,docker toolbox这个工具包里面集成了windows环境下运行docker必要的工具,当然也包括虚拟机了。

首先下载docker toolbox,
官方下载地址:https://www.docker.com/products/docker-desktop

执行安装程序,默认情况下,你的计算机会安装以下几个程序

  • Windows版的Docker客户端
  • Docker Toolbox管理工具和ISO镜像
  • Oracle VM 虚拟机
  • Git 工具

当然,如果你之前已经安装过了Oracle VM 虚拟机 或者 Git 工具 ,那么你在安装的时候可以取消勾选这两个内容,之后,你只需要狂点下一步即可。安装完毕以后,找到Docker Quickstart Terminal图标,双击运行,稍等它自己配置一小段时间,你会看到以下的界面

scrapy1.png

请注意上面画红框的地方,这是默认分配给你的ip,下面会用到。至此,docker工具就已经安装好了。

2、安装Splash

双击运行Docker Quickstart Terminal,输入以下内容

docker pull scrapinghub/splash 

这个命令是拉取Splash镜像,等待一算时间,就可以了。

下面就是启动Splash

docker run -p 8050:8050 scrapinghub/splash

这个命令就是在计算机的8050端口启动Splash渲染服务
你会看到以下的图示内容。

scrapy2.png

这个时候,打开你的浏览器,输入192.168.99.100:8050你会看到出现了这样的界面。

scrapy3.png

你可以在上图红色框框的地方输入任意的网址,点击后面的Render me! 来查看渲染之后的样子。

3、安装scrapy-splash

pip install scrapy-splash

至此,我们的准备环节已经全部结束了。

4、如果以上操作遇到困难

以上操作如遇到困难,请参考 Docker Toolbox for Windows 之安装二三事解决

三、实战爬取京东商品列表中的商品信息

网页地址:https://list.jd.com/list.html?cat=652,654,831

1、创建项目

scrapy startproject jdcamera

创建项目后在spider目录下新建 camera.py 文件

项目结构截图


scrapy4.png

2、items.py代码

import scrapy


class JdcameraItem(scrapy.Item):
    # 名称
    name = scrapy.Field()
    # 链接
    link = scrapy.Field()
    # 价格
    price = scrapy.Field()
    # 销售店铺
    owner = scrapy.Field()
    # 评论数
    comment = scrapy.Field()

3、camera.py代码

import scrapy
from jdcamera.items import JdcameraItem
from scrapy.http import Request
import re
from scrapy_splash import SplashRequest


class CameraSpider(scrapy.Spider):
    name = "jdcamera"
    allowed_domains = ["list.jd.com"]
    start_urls = (
        'https://list.jd.com/list.html?cat=652,654,831',
        # "https://list.jd.com/list.html?cat=652,654,831&page=1&sort=sort_totalsales15_desc&trans=1&JL=6_0_0#J_main"
    )

    def start_requests(self):
        for url in self.start_urls:
            yield SplashRequest(url=url, callback=self.parse,
                                args={'wait': 1}, endpoint='render.html')

    def parse(self, response):
        for sel in response.xpath('//*[@id="plist"]/ul/li/div[@class="gl-i-wrap j-sku-item"]'):
            item = JdcameraItem()
            # 链接
            item["link"] = "http:" + str(sel.xpath('div[1]/a/@href').extract())[2:-2]
            # 价格
            item["price"] = sel.xpath('div[2]/strong[1]/i/text()').extract()
            # 商品名称
            temp = str(sel.xpath('div[3]/a/em/text()').extract())
            pattern = re.compile("[\u4e00-\u9fa5]+.+\w")  # 从第一个汉字起 匹配商品名称
            good_name = re.search(pattern, temp)
            item["name"] = good_name.group()
            # 评论数
            item["comment"] = sel.xpath('div[4]/strong/a/text()').extract()
            # 销售店铺
            item["owner"] = sel.xpath('div[5]/span/a/text()').extract()
            # 提取完后返回item
            yield item

        # 通过循环自动爬取127页的数据
        for i in range(2, 128):
            # 通过上面总结的网址格式构造要爬取的网址
            url = "https://list.jd.com/list.html?cat=652,654,831&page=" + str(i) + "&sort=sort_totalsales15_desc&trans=1&JL=6_0_0"
            # 通过yield返回Request,并指定要爬取的网址和回调函数
            # 实现自动爬取
            # yield Request(url, callback=self.parse)
            yield SplashRequest(url=url, callback=self.parse, args={'wait': 1}, endpoint='render.html')

4、pipelines.py代码

import codecs
import json


class JdcameraPipeline(object):
    def __init__(self):
        # 此时存储到的文件是mydata2.json,不与之前存储的文件mydata.json冲突
        self.file = codecs.open('D:/DataguruPyhton/PythonSpider/images/京东数码相机.txt', "wb", encoding="utf-8")

    def process_item(self, item, spider):
        # 每一页中包含多个商品信息,所以可以通过循环,每一次处理一个商品
        # 其中len(item["name"])为当前页中商品的总数,依次遍历
        # 将当前页的第j个商品的名称赋值给变量name
        name = item["name"]
        price = item["price"]
        owner = item["owner"]
        comment = item["comment"]
        link = item["link"]
        # 将当前页下第j个商品的name、price、comnum、link等信息处理一下,重新组合成一个字典
        goods = {"name": name, "price": price, "owner": owner, "comment": comment, "link": link}
        # 将组合后的当前页中第j个商品的数据写入json文件
        i = json.dumps(dict(goods), ensure_ascii=False)
        line = i + '\n'
        self.file.write(line)
        # 返回item
        return item

    def close_spider(self, spider):
        self.file.close()

5、settings.py代码

BOT_NAME = 'jdcamera'

SPIDER_MODULES = ['jdcamera.spiders']
NEWSPIDER_MODULE = 'jdcamera.spiders'

# Crawl responsibly by identifying yourself (and your website) on the user-agent
USER_AGENT = 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36'

# Obey robots.txt rules
ROBOTSTXT_OBEY = False

# Disable cookies (enabled by default)
COOKIES_ENABLED = False

# Override the default request headers:
DEFAULT_REQUEST_HEADERS = {
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
    'Accept-Language': 'en',
    'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36"
}

# 渲染服务的url
SPLASH_URL = 'http://192.168.99.100:8050'

# Enable or disable downloader middlewares
# See https://doc.scrapy.org/en/latest/topics/downloader-middleware.html
DOWNLOADER_MIDDLEWARES = {
    'scrapy_splash.SplashCookiesMiddleware': 723,
    'scrapy_splash.SplashMiddleware': 725,
    'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware': 810,
}

ITEM_PIPELINES = {
    'jdcamera.pipelines.JdcameraPipeline': 300,  # 实现保存到txt文件
}

# 去重过滤器
DUPEFILTER_CLASS = 'scrapy_splash.SplashAwareDupeFilter'
# 使用Splash的Http缓存
HTTPCACHE_STORAGE = 'scrapy_splash.SplashAwareFSCacheStorage'

6、运行项目

scrapy crawl jdcamera

结果如图:


scrapy5.png

推荐阅读更多精彩内容