简介

谈起爬虫必然要提起 Scrapy 框架,因为它能够帮助提升爬虫的效率,从而更好地实现爬虫。

Scrapy 是一个为了抓取网页数据、提取结构性数据而编写的应用框架,该框架是封装的,包含:

request (异步调度和处理)、下载器(多线程的 Downloader)、解析器(selector)和 twisted(异步处理)等。

对于网站的内容爬取,其速度非常快捷。

官方文档:https://docs.scrapy.org/en/latest/

安装

一般推荐使用Anaconda一键安装

1
conda install -c conda-forge scrapy

这里给出常规pip安装的方法

【强烈推荐】使用**虚拟环境(venv)**进行开发,关于虚拟环境的相关内容见文章:

1
pip install Scrapy

如果遇见Twisted依赖包安装错误,这是因为包和python版本不匹配导致的。

可以进入镜像网站:https://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted 选择符合自己型号和版本的whl文件进行下载

然后cd至下载目录,通过pip install <文件名.whl>的方法安装

之后继续pip install Scrapy即可。

初始化

Scrapy提供了可操作性的 "项目 模式"对爬虫进行操作,避免了"一串代码从头写到尾"的各种弊端,符合模块化思想。

创建项目

1
scrapy startproject <项目名称>

cd进入项目所在路径后,执行命令以 创建爬虫

1
scrapy genspider <爬虫名称> <爬虫的主要url>

架构解释

  1. items.py: 用来存放爬虫爬取下来数据的模型
  2. middlewares.py: 用来存放各种中间件的文件
  3. pipelines.py: 用来将items的模型存储
  4. settings.py: 设置爬虫的一些配置信息(比如请求头、多久发送一次请求、ip代理池等)
  5. scrapy.cfg: 项目的配置文件
  6. spiders包: 以后所有的爬虫,都是存放到这个里面

基础配置

进入settings.py,为了更好进行爬虫,我们需要进行如下设置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 遵循 robots.txt 守则
# 默认为True,这里改为False
ROBOTSTXT_OBEY = False

# 设置下载停顿时间
# 默认被注释,如果爬取网站分页过多,为了不对所爬取的网站服务器造成压力,一般设置延迟
DOWNLOAD_DELAY = 3

# 定义请求头
DEFAULT_REQUEST_HEADERS = {
# 默认
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'en',
# 手动设置UA
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36',
}

# 开启 模型储存(如果没有保存数据的需求,可以保持注释)
# 这里以项目名为wxapp为例
ITEM_PIPELINES = {
'wxapp.pipelines.WxappPipeline': 300,
}

由于Scrapy框架运行爬虫程序的方式是通过命令行窗口输入命令:

1
scrapy crawl <爬虫名称>

此处,我们直接通过python语法将其写入python文件之中,以便方便运行。

<项目名称>/<项目名称>/下(与spiders文件夹同目录)新建文件run.py

1
2
3
4
5
6
7
8
from scrapy import cmdline

cmdline.execute("scrapy crawl <爬虫名称>".split())
# 该语句中,.split()相当于将str字符串分割为字符串列表
# 等价于:
# cmdline.execute(['scrapy','crawl','<爬虫名称>'])

# 注:上述语句中的尖括号不在实际代码中

具体使用

发起POST请求

有时候我们想要在请求数据的时候发送post请求,那么这时候需要使用Request 的子类FormRequest 来实现。

如果想要在爬虫一开始的时候就发送POST请求,那么就在爬虫类中重写start. requests(self) 方法,并且不再调用start. urls里的url

下面以 模拟登录人人影视为例,展示重写方法和发起POST请求的过程。

1
2
3
scrapy startproject rr_login
...
scrapy genspider rr "http://www.rrys2019.com/"

进入settings.py设置好后

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# spiders/rr.py

import scrapy

class RrSpider(scray.Spider):
name = 'renren'
allowed_domains = ['http://www.rrys2019.com/']
start_urls = ['http://www.rrys2019.com/']

#重写 开始方法(否则默认以get直接进入start_urls的地址,而不是登录)
def start_request(self):
# 登录url
url = "http://www.rrys2019.com/user/login"
# 设置登录数据
data = {
'account': 'yichuan@itd.cn',
'password': 'pyTHON123',
'remember': '1',
'url_back': 'http://www.rrys2019.com/'
}
# 发起请求(此处还设置了方法回调callback)
request = scrapy.FormRequest(url,formdata = data,callback = parse_page)
yield request

# 设置 解析页面数据的方法
def parse_page(self,response):
print('登录成功')

新建运行文件run.py

1
2
3
4
# run.py
from scrapy import cmdline

cmdline.execute("scrapr crawl rr".split())

之后运行即可!

CrawlSpider爬虫

简介

Crawlspider是Spider的一个子类;

Spider类的设计原则是只爬取start_url列表中的网页,而CrawlSpider类定义了一些规则(rule)来提供跟进link的方便的机制,从爬取的网页中获取link并继续爬取的工作更适合。

后者更适用于分页爬取和整站爬取项目

创建爬虫

前提:已经通过命令scrapy startproject创建好了项目

cd至工程文件目录下,使用以下命令代替之前的创建爬虫的命令:

1
scrapy genspider -t crawl <爬虫名称> <域名>

之后进入路径../<项目名称>/<项目爬虫>/spiders/下的爬虫文件爬虫名称.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# -*- coding: utf-8 -*-
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule


class 项目名称SpiderSpider(CrawlSpider):
name = '爬虫名字'
# url地址,以下以省略号带过
allowed_domains = ['..']
start_urls = ['..']

# 定义匹配规则
rules = (
Rule(LinkExtractor(allow=r'Items/'), callback='parse_item', follow=True),
)
# 解析数据(item可自定义)
def parse_item(self, response):
item = {}
return item

与Spider爬虫的区别

  • 建立爬虫方式不同
  • spider有parse函数,每次爬虫运行时会自动运行,而crawlspider没有,可自行通过callback回调函数

Rule规则类

链接提取器LinkExtractors

Scrapy Shell

我们一般在爬虫中使用xpathbeautifulsoup、 正则表达式、css选择器等来提取想要的数据。

但是因为scrapy 是个比较"重"的框架。每次运行起来都要等待一段时间。因此要去验证我们写的提取规则是否正确,是一个比较麻烦的事情。

所以Scrapy 提供了一个shell,用来方便测试规则。当然也不仅仅局限于这一个功能。

项目文件下,CMD内直接通过命令加想测试的url地址即可:

1
scrapy shell <url>

之后可通过python语法,按照自己的想法选取数据即可返回结果,用于测试规则是很方便的。如:

1
2
3
>>> title = response.xpath('//div[@class="dashabi"]//a/text()')
>>> title
>>> ……

除此之外,该shell也可以查看到我们settings中的各种设置等等

下载文件/图片

在爬虫基础篇中,我们已经给出了图片下载的方法。

其实,Scrapy集成有更加便捷和快速的方法以下载图片和文件。

  • 优点
    • 避免重新下载最近已经下载过的数据。
    • 可以方便指定文件存储的路径。
    • 可以将下载的图片转换成通用的格式。比如pngjpg
    • 可以方便的生成缩略图。
    • 可以方便的检测图片的宽和高,确保他们满足最小限制。
    • 异步下载,效率非常高。

下载前提

  • items.py中,定义好的两个属性:file_urls(给出列表)和files

    如果是图片,则分别是image_urlsimages

  • 文件下载完成后,系统会自动把文件下载的相关信息存储到itemfiles /images属性中。

    比如下载路径、下载的ur和文件/图片的校验码等。

  • 在配置文件settings.py 中配置FILES_STORE/IMAGE_STORE ,这个配置是用来设置文件/图片下载路径。

  • 启动pipeline :在ITEML PIPELINES中设置scrapy.pipelines.files.FilesPipeline:1

    图片则是scrapy.piplines.images.ImagespIPLINE:1

上手操作

以图片下载为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
# settings.py

import os

ITEM_PIPELINES = {
# 打开会运行主文件内的WxaooPipline函数
'wxapp.pipelines.WxappPipeline': 300,
# 设置如下
'scrapy.piplines.images.ImagespIPLINE': 1,
}

# 在底部添加
IMAGES_STORE = os.path.join(...)
1
2
# piplines.py

中间件

简介

随机请求头

随机代理

代理池

连接数据库

  • 所需库pymysql

  • 安装方法:pip install pymysql

  • 更多相关知识,详见数据库专题文章

由于连接数据库一般是将数据保存的操作,所以我们将数据库的连接部分放在piplines.py文件中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# 导包
import pymysql
# 在setting.py文件中,预先设置好相关信息,如host,port,user,passwd等
form Yourcrawl.setting import MYSQLDATA

# 此处是系统默认设置的管道对象,下面YourcrawlSpiderPipline中,Yourcrawl替换为自己的项目名称
class YourcrawlSpiderPipine(object):
def __init__(self):
# 设置表对象
self.conn = pymysql.connect(**MYSQLDATA)
# 设置游标
self.cursor = self.conn.cursor()
# 设置MySql语句
self._sql = None

@property
def sql(self):
# 使用装饰器,将sql()方法伪装成类以实现调用
if not self._sql:
self._sql='''
[此处为mysql语法的语句,表示想对数据库进行的操作]
'''
return self._dql
return self._sql
#如果没有sql语句就创建语句,有就直接返回

# 系统默认的item处理方法
def process_item(self,item,spider):
# 通过游标,运行sql语句
self.cursor.execute(self.sql,(其他参数))
# 此处的“其他参数”,其实是一个传递参数给self.sql的过程
# 当self.sql中的sql语句需要传递参数以保存内容时,如:
# insert into TEXT(id,name) values(%d,%s)
# 在TEXT表内插入数据,id=1,name='steve'.而1和steve可以在后面通过传参形式来补
# 注意,传入的是元组

# 提交
self.conn.commit()

当然,可以根据自己的需求优化代码,如结合python中的try-except设置savepointrollback

但是,当数据量过于庞大时,这种一个个数据进行存储的方式消耗内存,而且性能较差

这时我们还可以借助异步twisted(下载安装scrapy库时已集成)实现异步写入数据

关于异步

设置锁

   

异步写入数据库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# piplines.py

import pymysql
from twisted.enterprise import adbapi
from pymysql import cursors
from crawl.setting import MYSQLDATA

#创建新的类来对数据进行处理
#需要在setting.py中加入,并给出权重。当然,也可以直接在原class内改动
class YourCrawlTwistedPipline(object):
# 初始化
def __init__(self):
# 新增数据库游标class为字典型
MYSQLDATA['cursorclass'] = cursors.DictCursor
# 建立数据池
self.dbpool = adbapi.ConnectionPool('pymysql', **MYSQLDATA)
# 设置sql语句
self._sql = None

@property
def sql(self):
if not self._sql:
self._sql = '''
[此处为mysql语法的语句,表示想对数据库进行的操作,如:]
INSERT INTO TEXT(id,name) VALUES(%d,%s)
'''
return self._sql
return self._sql

def process_item(self,item,spider):
# 将名为inser_item的函数传递给runInteraction并实例化为defer使之进行异步操作
# 否则如果直接使用,与之前的同步操作数据库无异
defer = self.dbpool.runInteraction(self.insert_item,item)
# 当运行出错时,调用handle_error函数,显示错误信息
defer.addErrback(self.handle_error,item,spider)

#通过runInteraction调用的函数,还需要接收dbpool中之前我们定义的游标类型,sursorclass
#所以,此处参数还差一个cursor类型
def insert_item(self,cursor,item):
cursor.execute(self.sql,(元组参数))

def handle_error(self,error,item,spider):
# 当然,item和spider参数可以不传递,看需求
print("="*50,'!error!',"="*50)
print(error)
print("="*100)

Selenium 爬虫

爬取ajax数据

分布式爬虫

优点

参考资料

1.pip安装scrapy提示Twisted错误问题