文章目录
Scrapy 爬虫 网页分析入门实战
前言
刑啊,我们开始Python爬虫网页分析啦。由于某虫的叫法较为敏感(求生欲拉满),以下均将其称为网页分析吧!
说在前面
网页分析,是在实际工作中常见的需求之一,是通用的程序猿技能。我们多少都有必要掌握一些,这样在实际遇到这种需求时,可以快速上手。
本文基于对参考教程的学习,从脱敏示例的实战出发,直接演示Scrapy创建、网页分析、数据存储。篇幅有限,Scray简介、原理等概念,请读者自行查阅,本文和大家一起直接在实战中学习Scrapy如何使用。
学习目标
- 会用 Python 进行简单的网页分析实战,将分析到的数据存储到mysql数据表
注意,使用网页分析技术,请严格遵守相关法律法规,本文仅供技术学习与交流,代码例子均为虚拟的脱敏示例。
1. 创建Scrapy项目
创建及运行Scrapy项目的前提是您的机器原本就有python环境,需要使用pip安装。
1.1 下载Scrapy
首先我们要在本地安装Scrapy。命令如下:
pip install scrapy
验证安装是否成功
scrapy version
若出现Scrapy 版本,例如我的是:Scrapy 2.11.1
说明安装成功。
1.2 创建Scrapy项目
进入我们目标项目目录,执行
scrapy startproject <prject_name>
执行上述命令后,生成基础项目,里面包含了Scrapy项目的基本文件。
接下来,我们用pycharm等工具打开,然后需要注意一下是否设置了 python解释器。若没有设置,需要参考如下截图设置并生成.venv文件:
然后,在虚拟环境下再执行一遍:
pip install scrapy
这是为了在虚拟环境中也有scrapy相关依赖
1.3 创建Spider
terminal 执行 命令,参考如下:
scrapy genspider itcast "itcast.cn"
稍微解释一下,该命令的结构为:
scrapy genspider <spider_name> <domain>
domain为网站域名,意思为要分析的网站范围。后续的url都基于这个域名。要在实际业务使用的同学请将spider_name 和 domain 改成实际的。
那么此处我们就参照参考教程,爬取某客首页吧!
1.4 稍作修改并执行Demo
在上一小节执行了genspider
命令后,项目里又增加了一些py文件。有点像脚手架。
我们稍作修改,将response保存为文件:
def parse(self, response):
filename = "test.html"
open(filename, 'w').write(response.body)
pass
执行一下:
scrapy crawl itcast
若成功在项目内生成test.html文件,至此scrapy项目创建与spider创建成功!
2. 获取Cookie
分析一些网站,需要获取Cookie。通常,获取Cookie往往是开始爬虫的第一步。这里介绍我自己常用的两种获取Cookie方式
2.1 urllib获取Cookie
urllib获取Cookie
urllib 的方式获取Cookie也是一种模拟登录。若初学的时候觉得Scrapy模拟登录的方式比较绕,略显复杂,可以用 urllib 模拟登录。当然,在模拟登录前,我们首先需要自己实际在浏览器登录一波,获取登录请求url、表单提交格式等。
示例代码如下:
# 模拟登录获取cookie
def get_cookie():
cookie_list = []
try:
# 使用的时候具体地址替换为实际的url
url = "http://demo/url/login?xxx"
# 表单可以用字典的形式提交,根据实际填写各key value
data = {
"key_word": "1",
"password_secret": "08jsngalcoajv",
"username/account": "demo_account",
"password": "demo_password"
}
data = urllib.parse.urlencode(data).encode('utf-8')
req = urllib.request.Request(url, data=data, method='POST')
with urllib.request.urlopen(req) as response:
# 获取响应头中的 Cookie
cookies = response.headers.get('Set-Cookie')
if cookies:
cookie_list = cookies.split(', ')
else:
print("No cookies found in response headers")
except Exception as e:
print(e)
result = get_result(cookie_list)
cookies_dict = cookie_str_to_dict(result)
return cookies_dict
# 根据格式处理cookie
def get_result(cookie_list):
result = ""
if cookie_list:
# 拼接结果
result = "; ".join(cookie.split(';')[0] for cookie in cookie_list)
return result
# cookie进一步处理并以字典格式返回
def cookie_str_to_dict(cookie_str):
cookie_dict = {
}
cookies = cookie_str.split('; ')
for cookie in cookies:
key, value = cookie.split('=')
# 剔除部分不需要的cookie
if key != "Path":
cookie_dict[key] = value
# 根据实际执行结果,手动补充一些固定值(可选)
cookie_dict['acc'] = ''
cookie_dict['use_type'] = 1
return cookie_dict
另外,urllib的方式目前不支持微信、qq等登录。关于微信qq等第三方登录,需要另外想办法,这种登录方式后续再说。urllib支持表单填写post请求的方式。
start_requests携带Cookie
获取到cookie之后,接下来,就可以在具体scrapy xxx.py 里重写start_request方法,并携带cookie:
# 携带 cookie 直接访问目标页面
def start_requests(self):
cookies_dict = galaxy_governemnt_cookie.get_cookie()
for url in self.start_urls:
yield scrapy.Request(url, cookies=cookies_dict, callback=self.parse)
上述代码访问start_urls,并携带cookie,接着,回调函数指向parse方法,然后就可以愉快地在parse进行网页分析啦!
2.2 scrapy 模拟登录
我们也可以在scrapy的Spiders里进行模拟登录。登录后,再分析获取Cookie,之后,将Cookie带入下一步的请求,最后,再根据这个请求进行数据分析。
示例代码如下:
def start_requests(self):
# 使用的时候具体地址替换为实际的url
url = "http://demo/url/login?xxx"
# 表单可以用字典的形式提交,根据实际填写各key value
data = {
"key_word": "1",
"password_secret": "08jsngalcoajv",
"username/account": "demo_account",
"password": "demo_password"
}
yield scrapy.FormRequest(url, formdata=data, callback=self.parse)
def parse(self, response, *args, **kwargs):
# 下一步操作,先从response里拿到Cookie信息,具体代码略.....
pass
使用 Scrapy 进行模拟登录,建议使用 scrapy.FormRequest(url, formdata=data, [callback=<def_method>])
来进行from表单的提交方式。
这种获取Cookie的方式在少量的爬虫项目里可以使用。若项目比较大,需要爬取的地方比较多,那么每次执行parse之前都要提交一次登录请求,这样整个项目难免会有部分代码冗余。这时候,就建议使用统一封装好的方法获取Cookie了。
3. 分析
具体网页分析需要2大步骤:数据建模与获取数据。
数据建模说白了就是定义一个类去接收获取到的数据,而获取数据时,需要我们有一定的xpath或CSS基础,通过xpath与选择器获取我们想要的数据了。
下面我们介绍具体分析的数据建模与获取数据步骤:
3.1 数据建模
数据建模需要我们在项目里 items.py 里新建一个类:
示例代码如下:
class DemoSpiderItem(scrapy.Item):
# 代码
code = scrapy.Field()
# 使用类型
use_type = scrapy.Field()
# 使用编号
use_num = scrapy.Field()
注意,这个类首先要继承scrapy.Item类,然后每个字段都要调用scrapy.Field()。语义上,这表示这个类的字段值都来自爬取的字段。
至于源码,我还没仔细研究,有兴趣的读者可以去研究一下。
3.2 获取数据
获取数据就在parse以及parse回调的方法里实现了。
我们直接看示例,假设我们要获取一个列表:
def parse(self, response, *args, **kwargs):
# 目标列表
result_list = response.xpath('//*[@id="ec_table_body"]/tr')
# 遍历列表,分析每一行元素
for result in result_list:
item = DemoSpiderItem()
item['code'] = result.xpath('./tr[1]/td[3]').extract_first()
item['use_type'] = result.xpath('./tr[1]/td[3]').extract_first()
yield item
通过示例,我们总结的流程是:分析要获取的页面元素xpath(或选择器),接着,用我们建模定义的数据去接收。
4. 管道
4.1 管道使用流程
Scrapy 框架已经帮我们将数据从爬虫逻辑中传输到管道。那么接下来的逻辑就在管道中处理即可。使用 Scrapy 管道主要有以下三步:创建管道、编写管道逻辑、开启管道。
下面我们逐一介绍。
4.1.1 创建管道
class GalaxyHnbcSpiderPipeline:
# 定义用于接收 item 字典列表
def __init__(self):
self.item_list = []
@staticmethod
def open_spider(spider):
if spider.name == "TestSpider":
def process_item(self, item, spider):
if spider.name == "TestSpider":
# 将item转换为字典
data = dict(item)
# 将 data 加入 item_list
self.item_list.append(data)
return item
def close_spider(self, spider):
# 数据存储逻辑
.....
创建管道主要通过spider名来判断具体执行哪个管道。其中,创建的管道主要有三部分:open_spider、process_item以及close_spider。
4.1.2 管道逻辑
管道的逻辑主要作数据处理,例如,什么数据插入,什么数据更新,要转换成什么类型等等。
4.1.3 开启管道
管道以及管道的逻辑编辑完成后,我们还要再settings.py开启管道。序号参数越小代表优先级越高,当前版本的Scrapy配置管道相关在settings.py的67行,序号默认为300,不建议序号超过1000:
# Configure item pipelines
# See https://docs.scrapy.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {
"TestServer.pipelines.TestSpiderPipeline": 299,
}
4.2 持久化
有时,我们爬取的数据需要根据实际需求来进行存储,例如:为了保持数据业务的完整性、一致性,需要先将库里的数据对Scrapy爬取下来的数据进行一个对比,再进行持久化操作。
这里我采用SQLAlchemy连接数据库,在管道结束时将数据比对并插入数据库:
存储逻辑
def close_spider(self, spider):
if spider.name == "TestSpider":
spider_list = []
for temp_dict in self.item_list:
# 将主键的值设置为空
temp_dict['pid'] = None
demo_calss = DemoClass(temp_dict)
spider_list.append(demo_calss)
# 数据存储逻辑
if spider_list:
with Session(db_init.engine) as session:
exist_list = session.query(GalaxyHnbc).all()
insert_list = spider_list
# 删除列表:数据库里有的,而爬虫结果没有的
delete_list = []
# 更新列表:爬虫结果和数据库里都有的,不管一不一样直接更新
update_list = []
# 执行新增
if insert_list:
session.add_all(insert_list)
session.commit()
SqlAlchemy
关于SqlAlchemy,是一种可选的持久化方式。当然读者也可以选择其它连接数据库的方式,将爬取的数据保存下来。
5. 补充与总结
5.1 Scrapy下载/创建过程中遇到的问题
本次我个人在Windows执行scrapy version时报错了,大家遇到的各种错误与原因都可能不相同,我本次遇到的问题参考以下解决方案自行解决:
- https://blog.csdn.net/weixin_38708145/article/details/130219078
- https://blog.csdn.net/yujinlong2002/article/details/126660363
5.2 分页/多级页面解决方案
当我们要分析的是分页/多级页面时,可以在start_requests方法里先进行页面的预处理,也就是说,先手动分析分页的参数,然后在start_requests方法里事先拼接好二级页面的url参数,然后再通过循环等方式请求,将具体的response交给parse方法解析。
如下示例:
def start_requests(self):
cookies_dict = target_website_cookie.get_cookie()
# 二级页面初始url
secondary_start_url = ("http://demo/url")
# 定义 二级页面 url 所需参数 字典 数组
secondary_params = []
# 访问一级页面获取关键信息
start_response = scrapy.Request(self.start_urls[0], cookies=cookies_dict)
for element in start_response.xpath('//tr[onclick^=ECSideUtil.selectRow]'):
tds = element.xpath('td')
# 提取do_edit的参数
secondary_id = tds[2].xpath('./input[@onclick]/@onclick').get().split("'")[1]
# 提取其它信息
review_status = tds[9].xpath('text()').get()
# 将id和其它信息存入数组
secondary_params.append({
"secondary_id": secondary_id, "review_status": review_status})
for secondary_param in secondary_params:
# 拼接二级页面url
secondary_url = secondary_start_url + secondary_param["secondary_id"]
# 携带 cookie 直接访问目标页面
yield scrapy.Request(secondary_url, cookies=cookies_dict, callback=self.parse, meta=secondary_param)
5.3 总结
本文主要基于个人的实际开发,总结了Scrapy的基本用法:
- 项目配置、创建
- Spider 网页分析的使用
- 数据建模
- 管道的使用
- 数据持久化策略
感谢大家阅读此文,希望我的内容能够给您带来启发和帮助。如果您喜欢我的文章,欢迎点赞、评论和分享,让更多人看到。期待与您在下一篇文章再相会!