Python 全栈之路系列之 scrapy 爬虫
An open source and collaborative framework for extracting the data you need from websites.
官网:https://scrapy.org GITHUB 地址:https://github.com/scrapy/scrapy
Scrapy 运行流程大概如下:
- 引擎从调度器中取出一个链接(URL)用于接下来的抓取
- 引擎把 URL 封装成一个请求(Request)传给下载器
- 下载器把资源下载下来,并封装成应答包(Response)
- 爬虫解析 Response
- 解析出实体(Item),则交给实体管道进行进一步的处理
- 解析出的是链接(URL),则把 URL 交给调度器等待抓取
# 安装
因为我是 Ubuntu 系统,所以可以直接通过 pip 安装 scrapy
pip install scrapy
1
# 使用
创建项目
scrapy startproject xiaohuar
1
目录结构
⇒ tree xiaohuawang
xiaohuawang
# 项目的配置信息,主要为Scrapy命令行工具提供一个基础的配置信息。(真正爬虫相关的配置信息在settings.py文件中)
├── scrapy.cfg
└── xiaohuawang
├── __init__.py
# 设置数据存储模板,用于结构化数据
├── items.py
# 数据处理行为,如:一般结构化的数据持久化
├── pipelines.py
├── __pycache__
# 配置文件,如:递归的层数、并发数,延迟下载等
├── settings.py
# 爬虫目录,如:创建文件,编写爬虫规则
└── spiders
├── __init__.py
└── __pycache__
4 directories, 6 files
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
编写爬虫
创建文件:"xiaohuar/xiaohuar/spiders/myspider.py"
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import scrapy
class XiaoHuarSpider(scrapy.spiders.Spider):
name = "xiaohuar" # APP的名字,必须定义
start_urls = [
"http://www.xiaohuar.com/hua/", # 起始URL
]
def parse(self, response): # 抓取start_urls页面,自动执行parse回调函数
current_url = response.url # 当前请求的URL
body = response.body # 请求的内容
unicode_body = response.body_as_unicode() # 编码
print(body)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
运行
进入 xiaohuar 目录,运行命令
scrapy runspider myspider.py --nolog # 不输出debug日志
1
一个抓取图片的小实例
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import scrapy
import os
import urllib
from scrapy.selector import HtmlXPathSelector
class XiaoHuarSpider(scrapy.spiders.Spider):
name = "xiaohuar" # APP的名字,必须定义
start_urls = [
"http://www.xiaohuar.com/hua/", # 起始URL
]
def parse(self, response): # 抓取start_urls页面,自动执行parse回调函数
hxs = HtmlXPathSelector(response) # 匹配查找
items = hxs.select('//div[@class="item_list infinite_scroll"]/div')
for i in range(len(items)):
srcs = hxs.select(
'//div[@class="item_list infinite_scroll"]/div[%d]//div[@class="img"]/a/img/@src' % i).extract()
names = hxs.select(
'//div[@class="item_list infinite_scroll"]/div[%d]//div[@class="img"]/span/text()' % i).extract()
schools = hxs.select(
'//div[@class="item_list infinite_scroll"]/div[%d]//div[@class="img"]/div[@class="btns"]/a/text()' % i).extract()
if srcs and names and schools:
# print(names, srcs, schools)
# ['覃罗莹'] ['/d/file/20161018/5385b7113046ac9ae560da41a44b12af.jpg'] ['广西农业职业技术学院']
try:
ab_src = "http://www.xiaohuar.com" + srcs[0] # 文件路径
file_name = names[0] + "." + srcs[0].split(".")[-1] # 保存的文件名
file_path = os.path.join("./pic", file_name) # 保存的路径
# print(ab_src, file_name, file_path)
# http://www.xiaohuar.com/d/file/20161018/5385b7113046ac9ae560da41a44b12af.jpg 覃罗莹jpg ./pic/覃罗莹jpg
urllib.request.urlretrieve(ab_src, file_path) # 下载文件
except Exception as e:
print("错误》》", e)
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
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
# 选择器
基本的选择器
选择器 | 描述 |
---|---|
// | 子子孙孙 |
/ | 孩子 |
//div[@class="c1"][@id='i1'] | 属性选择器 |
//div//img/@src | div 下所有的 img 属性 src |
//div//a[1] | 索引取值 |
//div//a[1]//text() | 索引取值的内容 |
通过 extract 获取真实的数据:
//div[@class="c1"][@id='i1'].extract()
1
支持正则
选择器 | 描述 |
---|---|
//.select("div//a[1]").re("昵称:(\w+)") | 正则 |
官方文档:http://scrapy-chs.readthedocs.io/zh_CN/latest/topics/selectors.html
两种查找方式
# 即将被废弃的
from scrapy.selector import HtmlXPathSelector
hxs = HtmlXPathSelector(response)
items_HtmlXPathSelector = hxs.select('//div[@class="item_list infinite_scroll"]/div')
print(len(items_HtmlXPathSelector))
from scrapy.selector import Selector
items_Selector = Selector(response=response).xpath('//div[@class="item_list infinite_scroll"]/div')
print(len(items_Selector))
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
正则表达式实例
<body>
<li class="item-"><a href="link.html">first item</a></li>
<li class="item-0"><a href="link1.html">first item</a></li>
<li class="item-1"><a href="link2.html">second item</a></li>
</body>
1
2
3
4
5
2
3
4
5
ret = Selector(response=response).xpath('//li[re:test(@class, "item-\d*")]//@href').extract()
# re -- 通过正则进行匹配
# test -- 匹配
1
2
3
2
3
# 扩展
重复的 URL 不访问
先把长的 URL 进行 MD5 加密,加密成 32 或者 64 位,可以保存在一个集合或者缓存、数据库中,每次抓取之前都先判断有没有这个 URL。
递归查找
设置查找深度:修改settings.py
配置文件,加入以下参数指定深度DEPTH_LIMIT = 1
内容格式化
就是相当于分类,比如说下面的文件:
文件 | 功能 |
---|---|
myspider.py | 查找 URL 的规则 |
items.py | 数据 |
pipelines.py | 数据持久化 |
如图所示:
编辑 (opens new window)
上次更新: 2024-07-15, 03:27:09