一、概述

Scrapy,Python开发的一个快速、高层次的屏幕抓取和web抓取框架,用于
抓取web站点并从页面中提取结构化的数据。Scrapy用途广泛,可以用于数
据挖掘、监测和自动化测试.
其最初是为了页面抓取 (更确切来说, 网络抓取 )所设计的, 后台也应用在
获取API所返回的数据(例如 Amazon Associates Web Services ) 或者通用的网
络爬虫.
Scrapy吸引人的地方在于它是一个框架,任何人都可以根据需求方便的修
改。它也提供了多种类型爬虫的基类,如B
新版本又提供了web2.0爬虫的支持.

二、Scrapy五大基本构成:

Scrapy框架主要由五大组件组成,它们分别是调度器(Scheduler)、
下载器(Downloader)、爬虫(Spider)和实体管道(Item Pipeline)、
Scrapy引擎(Scrapy Engine)。下面我们分别介绍各个组件的作用。
(1)、调度器(Scheduler):
调度器,说白了把它假设成为一个URL(抓取网页的网址或者说是链接)的优先队列,由它来决定
下一个要抓取的网址是 什么,同时去除重复的网址(不做无用功)。用户可以自己的需求定制调
度器。
(2)、下载器(Downloader):
下载器,是所有组件中负担最大的,它用于高速地下载网络上的资源。Scrapy的下载器代码不会太
复杂,但效率高,主要的原因是Scrapy下载器是建立在twisted这个高效的异步模型上的(其实整个
框架都在建立在这个模型上的)。
(3)、 爬虫(Spider):
爬虫,是用户最关心的部份。用户定制自己的爬虫(通过定制正则表达式等语法),用于从特定的网
页中提取自己需要的信息,即所谓的实体(Item)。 用户也可以从中提取出链接,让Scrapy继续抓取下
一个页面。
(4)、 实体管道(Item Pipeline):
实体管道,用于处理爬虫(spider)提取的实体。主要的功能是持久化实体、验证实体的有效性、清
除不需要的信息。
(5)、Scrapy引擎(Scrapy Engine):
Scrapy引擎是整个框架的核心.它用来控制调试器、下载器、爬虫。实际上,引擎相当于计算机的
CPU,它控制着整个流程。

安装:scrapy(windows cmd 输入以下指令)
python -m pip install --upgrade pip
pip install wheel
pip install lxml
pip install twisted
pip install pywin32
pip install scrapy

1.创建项目
打开一个终端输入(建议放到合适的路径下,默认是C盘)
cd desktop
scrapy startproject TXmovies
cd TXmovies
scrapy genspider txms v.qq.com
2.修改setting
修改三项内容,第一个是不遵循机器人协议,第二个是下载间隙,由于下面的程序要下载多个页面,所以需要给一个间隙(不给也可以,只是很容易被侦测到),第三个是请求头,添加一个User-Agent,第四个是打开一个管道
ROBOTSTXT_OBEY = False

DOWNLOAD_DELAY = 1

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 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.94 Safari/537.36’
}

ITEM_PIPELINES = {
‘TXmovies.pipelines.TxmoviesPipeline’: 300,
}

3.确认要提取的数据,item项
item定义你要提取的内容(定义数据结构),比如我提取的内容为电影名和电影描述,我就创建两个变量。Field方法实际上的做法是创建一个字典,给字典添加一个建,暂时不赋值,等待提取数据后再赋值。下面item的结构可以表示为:{‘name’:‘’,‘descripition’:‘’}。
**# -- coding: utf-8 --

# Define here the models for your scraped items

# See documentation in:
# https://docs.scrapy.org/en/latest/topics/items.html**

import scrapy

class TxmoviesItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
name = scrapy.Field()
description = scrapy.Field()
4.写爬虫程序
我们要写的部分是parse方法里的内容,重点在于如何写xpath,关于xpath我不多讲,有兴趣可以看看我另一篇文章,XPATH教程
引入刚刚写好的item,刚刚说了item里面创建的变量就是字典的键值,可以直接进行赋值。赋值后交给管道处理。
简单讲一下这一段代码的思路,首先腾讯视频的url为https://v.qq.com/x/bu/pagesheet/list?append=1&channel=cartoon&iarea=1&listpage=2&offset=0&pagesize=30
我们注意到offset这一项,第一页的offset为0,第二页为30,依次推列。在程序中这一项用于控制抓取第一页,但是也要给一个范围,不可能无限大,否则会报错,可以去看看腾讯一共有多少页视频,也可以写一个异常捕获机制,捕捉到请求出错则退出。我这里仅仅是示范,所以只给了120,也就是4页。

yield
程序里一共有两个yield,我比较喜欢叫它中断,当然中断只在CPU中发生,它的作用是移交控制权,在本程序中,我们对item封装数据后,就调用yield把控制权给管道,管道拿到处理后return返回,又回到该程序。这是对第一个yield的解释。
第二个yield稍微复杂点,这条程序里利用了一个回调机制,即callback,回调的对象是parse,也就是当前方法,通过不断的回调,程序将陷入循环,如果不给程序加条件,就会陷入死循环,如本程序我把if去掉,那就是死循环了。
yield scrapy.Request(url=url,callback=self.parse)

xpath
还有一个要注意的是如何提取xpathl里的数据,我们的写法有四种,第一种写法拿到selector选择器,也就是原数据,里面有一些我们用不到的东西。第二个extract(),将选择器序列号为字符串。第三个和第四个一样,拿到字符串里的第一个数据,也就是我们要的数据。
items[‘name’]=i.xpath(‘./a/@title’)[0]
items[‘name’]=i.xpath(‘./a/@title’).extract()
items[‘name’]=i.xpath(‘./a/@title’).extract_first()
items[‘name’]=i.xpath(‘./a/@title’).get()
# -- coding: utf-8 --
import scrapy
from …items import TxmoviesItem

class TxmsSpider(scrapy.Spider):
name = ‘txms’
allowed_domains = [‘v.qq.com’]
start_urls = [‘https://v.qq.com/x/bu/pagesheet/list?append=1&channel=cartoon&iarea=1&listpage=2&offset=0&pagesize=30’]
offset=0

def parse(self, response):
    items=TxmoviesItem()
    lists=response.xpath('//div[@class="list_item"]')
    for i in lists:
        items['name']=i.xpath('./a/@title').get()
        items['description']=i.xpath('./div/div/@title').get()

        yield items

    if self.offset < 120:
        self.offset += 30
        url = 'https://v.qq.com/x/bu/pagesheet/list?append=1&channel=cartoon&iarea=1&listpage=2&offset={}&pagesize=30'.format(
            str(self.offset))

        yield scrapy.Request(url=url,callback=self.parse)

5.交给管道输出
管道可以处理提取的数据,如存数据库。我们这里仅输出。
**# -- coding: utf-8 --

# Define your item pipelines here

# Don’t forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://docs.scrapy.org/en/latest/topics/item-pipeline.html**

class TxmoviesPipeline(object):
def process_item(self, item, spider):
print(item)
return item
6.run,执行项目

使用终端运行太麻烦了,而且不能提取数据,
我们一个写一个run文件作为程序的入口,splite
是必须写的,目的是把字符串转为列表形式,
第一个参数是scrapy,第二个crawl,第三个baidu
from scrapy import cmdline
 
cmdline.execute('scrapy crawl baidu'.split())
创建一个名称为run的.py文件,执行项目
from scrapy import cmdline
 
cmdline.execute('scrapy crawl txms'.split())

7.测试结果
白色的管道输出的结果,红色的调试信息

8.流程梳理
新建项目-》进入项目-》新建爬虫文件-》明确抓取的内容,写item-》写爬虫程序,爬取数据-》交给管道处理数据-》调整全局配置setting-》执行爬虫程序,可以通过终端或者在程序里写一个run程序

9.提速:多线程爬取
如果你实现了上面的实验,不难发现其爬取速度是非常慢,根本的原因就是因为它是顺序执行的,你可以从结果中看出,总是前面一页的内容被输出,再输出后面的内容。不适合处理数据量较大的情况,一个好的方式是采用多线程的方法,这里的多线程是基于方法的多线程,并不是通过创建Thread对象来实现,是在一个方法中,一次性把请求交给调度器。
我们通过重写start_requests方法来实现我们的想法(这个方法的源码在__init__.py下面,有兴趣可以看一下)
# -- coding: utf-8 --
import scrapy
from …items import TxmoviesItem

class TxmsSpider(scrapy.Spider):
name = ‘txms’
allowed_domains = [‘v.qq.com’]
url=‘https://v.qq.com/x/bu/pagesheet/list?append=1&channel=cartoon&iarea=1&listpage=2&offset={}&pagesize=30’
offset=0

def start_requests(self):
    for i in range(0,121,30):
        url=self.url.format(i)
        yield scrapy.Request(
            url=url,
            callback=self.parse
        )

def parse(self, response):
    items=TxmoviesItem()
    lists=response.xpath('//div[@class="list_item"]')
    for i in lists:
        items['name']=i.xpath('./a/@title').get()
        items['description']=i.xpath('./div/div/@title').get()

        yield items