Scrapy详解 爬虫框架入门看这一篇就够了!

2019-07-17  本文已影响0人  所謂向日葵族

前言

学习Scrapy有一段时间了,当时想要获取一下百度汉字的解析,又不想一个个汉字去搜,复制粘贴太费劲,考虑到爬虫的便利性,这篇文章是介绍一个爬虫框架--Scrapy,非常主流的爬虫框架,写爬虫还不会Scrapy,你就out啦🙈~

🐞爬虫的应用场景:

架构

官方解析:Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架。可以应用在包括数据挖掘,信息处理或存储历史数据等一系列的程序中。

其最初是为了页面抓取(更确切来说,网络抓取)所设计的,也可以应用在获取API所返回的数据或者通用的网络爬虫。

Scrapy架构图.png

架构分析

数据处理流程
1、引擎打开一个网站(open a domain),找到处理该网站的Spider并向该Spider请求要爬取的第一个start_urls。
2、引擎从Spider中获取到第一个要爬取的URL并在调度器(Scheduler)以Request调度。
3、引擎向调度器请求下一个要爬取的URL。
4、调度器返回下一个要爬取的URL给引擎,引擎将URL通过Downloader Middlewares(request)转发给下载器(Downloader)。
5、一旦页面下载完毕,Downloader生成一个该页面的Response,并将其通过Downloader Middlewares(response)发送给引擎。
6、引擎从Downloader中接收到Response并通过Spider Middlewares(request)发送给Spider处理。
7、Spider处理Response并返回爬取到的Item及(跟进的)新的Request给引擎。
8、引擎将(Spider返回的)爬取到的Item给Item Pipeline,将(Spider返回的)Request给调度器。
9、系统重复2-9的操作,直到调度中没有更多地request,然后断开引擎与网站之间的联系。

安装

依赖环境:

使用pip安装:

pip install Scrapy

创建项目:

scrapy startproject [项目名]

如创建 scrapy startproject qimairank,会自动创建Scrapy的项目架构:

qimairank

|--qimairank
    |--spiders
        |--__init__.py
    |--__init__.py
    |--items.py
    |--middlewares.py
    |--pipelines.py
    |--settings.py
|--scrapy.cfg

第一个爬虫:爬取有道翻译

熟悉Scrapy框架后,我们手写第一个爬虫,爬取有道翻译的单词发音,发音文件链接,释义,例句。

如单词proportion:有道翻译的详情连接为 http://dict.youdao.com/w/eng/proportion 。本篇文章爬取的内容结果:

{"example": [{"en": "I seemed to have lost all sense of proportion.",
              "zh": "我好象已经丧失了有关比例的一切感觉。"},
             {"en": "The price of this article is out of(all) proportion to its value.",
              "zh": "这个商品的价格与它的价值完全不成比例。"},
             {"en": "But, the use of interception bases on the violation of the citizen rights, so it should be satisfactory of the principle of legal reservation and the principle of proportion.",
              "zh": "但是,监听的适用是以侵害公民权利为前提的,因此监听在刑事侦查中的运用必须满足法律保留原则和比例原则的要求。"}],
 "explain": ["n. 比例,占比;部分;面积;均衡", "vt. 使成比例;使均衡;分摊"],
 "pron": "[prə'pɔːʃ(ə)n]",
 "pron_url": "http://dict.youdao.com/dictvoice?audio=proportion&type=1",
 "word": "proportion"}
proportion有道释义.jpg

创建项目

在需要创建的目录下,

scrapy startproject youdaoeng

回车即可创建默认的Scrapy项目架构。

youdaoeng项目架构.jpg

创建Item

创建YoudaoengItem继承scrapy.Item,并定义需要存储的单词,发音,发音文件链接,释义,例句。

import scrapy


class YoudaoengItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    # 单词
    word = scrapy.Field()
    # 英式发音
    pron = scrapy.Field()
    # 发音audio文件链接
    pron_url = scrapy.Field()
    # 释义
    explain = scrapy.Field()
    # 例句
    example = scrapy.Field()

创建Spider

spiders目录下创建EngSpider.py,并创建class EngSpider,继承于Spider。

from scrapy import Spider


class EngSpider(Spider):
    name = "EngSpider"
    # 允许访问的域
    allowed_domains = ["dict.youdao.com"]

    start_urls = [
        'http://dict.youdao.com/w/eng/agree', 'http://dict.youdao.com/w/eng/prophet',
                'http://dict.youdao.com/w/eng/proportion']

    def parse(self, response):
        pass

有道翻译的网址为http://dict.youdao.com/ ,根据分析,查询英文单词结果后链接更改,如查询agree,跳转单词详情地址为http://dict.youdao.com/w/eng/agree 。所以几乎可以认为单词的详情页链接可以是http://dict.youdao.com/w/eng/ 拼接上单词本身,所以配置start_urls我们查询三个单词的释义详情。

解析

解析用的Selectors选择器有多种方法:

下面我们用xpath()选择节点,xpath的语法可参考w3c的http://www.w3school.com.cn/xpath/xpath_nodes.asp 学习,需要熟悉语法、运算符、函数等。

    def parse(self, response):
        box = response.xpath('//*[@id="results-contents"]')
        word = YoudaoengItem()
        # 简明释义
        box_simple = box.xpath('.//*[@id="phrsListTab"]')
        # 判断查出来的字是否存在
        if box_simple:
            # 单词
            word['word'] = box_simple.xpath('.//h2[@class="wordbook-js"]//span[@class="keyword"]/text()').extract()[0]
            # 英式发音
            word['pron'] = box_simple.xpath(
                './/h2[@class="wordbook-js"]//div[@class="baav"]//*[@class="phonetic"]/text()').extract()[0]
            # 发音链接
            word['pron_url'] = "http://dict.youdao.com/dictvoice?audio=" + word['word'] + "&type=1"
            # 释义
            word['explain'] = []
            temp = box_simple.xpath('.//div[@class="trans-container"]//ul//li/text()').extract()
            for item in temp:
                if len(item) > 0 and not re.search(r'\n', item) and not re.match(r' ', item):
                    print(item)
                    word['explain'].append(item)
            # 例句
            time.sleep(3)
            word['example'] = []
            example_root = box.xpath('//*[@id="bilingual"]//ul[@class="ol"]/li')
            # 1.双语例句是否存在
            if example_root:
                for li in example_root:
                    en = ""
                    for span in li.xpath('./p[1]/span'):
                        if span.xpath('./text()').extract():
                            en += span.xpath('./text()').extract()[0]
                        elif span.xpath('./b/text()').extract():
                            en += span.xpath('./b/text()').extract()[0]
                    zh = str().join(li.xpath('./p[2]/span/text()').extract()).replace(' ', '')
                    word['example'].append(dict(en=en.replace('\"', '\\"'), zh=zh))
            #  2.柯林斯英汉双解大辞典的例句是否存在
            elif box.xpath('//*[@id="collinsResult"]//ul[@class="ol"]//div[@class="examples"]'):
                example_root = box.xpath('//*[@id="collinsResult"]//ul[@class="ol"]//li')
                for i in example_root:
                    if i.xpath('.//*[@class="exampleLists"]'):
                        en = i.xpath(
                            './/*[@class="exampleLists"][1]//div[@class="examples"]/p[1]/text()').extract()[0]
                        zh = i.xpath(
                            './/*[@class="exampleLists"][1]//div[@class="examples"]/p[2]/text()').extract()[0]
                        word['example'].append(dict(en=en.replace('\"', '\\"'), zh=zh))
                        if len(word['example']) >= 3:
                            break
            yield word

最后 yield word则是返回解析的word 给Item Pipeline,进行随后的数据过滤或者存储。

运行爬虫-爬取单词释义

运行爬虫,会爬取agree、prophet、proportion三个单词的详情,在项目目录下(scrapy.cfg所在的目录)

youdaoeng>scrapy crawl EngSpider -o data.json

即可运行,窗口可以看见爬取的日志内容输出,运行结束后会在项目目录下生成一个data.json文件。


开始爬取.jpg Item输出.jpg 生成的data.json.jpg

生成的数据为所有item的json格式数组,中文字符都是Unicode编码,可通过一些在线的json解析网站如 https://www.bejson.com/ ,Unicode转中文查看是我们想要的结果。

下载单词语音文件

单词读音的mp3链接为解析时候保存的pron_url字段,接下来我们下载单词mp3文件到本地。
在Item下增加属性pron_save_path,存储发音文件的本地地址:

# 发音 mp3 本地存放路径
    pron_save_path = scrapy.Field()

并在settings.py文件中配置下载文件的目录,如在D:\scrapy_files\目录下,则配置

FILES_STORE = "D:\\scrapy_files\\"

增加ItemPipeline重新发起文件下载请求:

class Mp3Pipeline(FilesPipeline):
    '''
    自定义文件下载管道
    '''

    def get_media_requests(self, item, info):
        '''
        根据文件的url发送请求(url跟进)
        :param item:
        :param info:
        :return:
        '''
        # meta携带的数据可以在response获取到
        yield scrapy.Request(url=item['pron_url'], meta={'item': item})

    def item_completed(self, results, item, info):
        '''
        处理请求结果
        :param results:
        :param item:
        :param info:
        :return:
        '''
        file_paths = [x['path'] for ok, x in results if ok]
        if not file_paths:
            raise DropItem("Item contains no files")

        # old_name = FILES_STORE + file_paths[0]
        # new_name = FILES_STORE + item['word'] + '.mp3'

        # 文件重命名 (相当于剪切)
        # os.rename(old_name, new_name)

        # item['pron_save_path'] = new_name

        # 返回的result是除去FILES_STORE的目录
        item['pron_save_path'] = FILES_STORE + file_paths[0]
        return item

    def file_path(self, request, response=None, info=None):
        '''
        自定义文件保存路径
        默认的保存路径是在FILES_STORE下创建的一个full来存放,如果我们想要直接在FILES_STORE下存放,则需要自定义存放路径。
        默认下载的是无后缀的文件,需要增加.mp3后缀
        :param request:
        :param response:
        :param info:
        :return:
        '''
        file_name = request.meta['item']['word'] + ".mp3"
        return file_name

需要更改settings.py文件,配置Mp3Pipeline,后面的300为优先级,数字越大,优先级越低。

# Configure item pipelines
# See https://doc.scrapy.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {
    # 'youdaoeng.pipelines.YoudaoengPipeline': 300,
    'youdaoeng.pipelines.Mp3Pipeline': 300,
}

运行

youdaoeng>scrapy crawl EngSpider -o data1.json

等待运行完成,则在项目目录下生成了data1.json,并在D:\scrapy_files\目录下生成了我们爬取的三个单词的释义。

项目源码

上一篇 下一篇

猜你喜欢

热点阅读