爬虫框架Scrapy
- 创建项目
scrapy startproject 项目名称
- 创建爬虫
cd 项目名称
scrapy genspider example example.com
scrapy结构
项目文件夹
- 同名文件夹
- spiders
- init
- 爬虫文件
- init
- items.py
- middlewares.py
- pipelines.py
- settings.py
- spiders
- scrapy.cfg
文件作用
spider下的爬虫文件:
- 爬虫文件名称
- 访问文件约束
- 开始的URL地址
- 获取请求返回的数据提取后 yield返回给pipelines.py处理
pipelines.py
- 可以进行接收处理保存数据
- 注:如果有多个爬虫文件返回数据可以分开处理,一个爬虫项目包含多个爬虫
- 需要在settings.py设置,多个pipelines.py开启多个,数字小先访问
ITEM_PIPELINES = {
'myspider.pipelines.MyspiderPipeline': 300,
'myspider.pipelines.MyspiderPipeline1': 301,
}
多个爬虫文件爬取多个网站 pipelines.py处理
- item中添加字段标记来源,pipelines.py进行判断
- 可以在一个class中处理,也可以在多个class中处理
class MyspiderPipeline(object):
def process_item(self, item, spider):
if xxx["from"] == "JD"
···
return item
class MyspiderPipeline1(object):
def process_item(self, item, spider):
if itcst["from"] == "TB"
对spider进行判断 if spider.name == "xxxx"
pipelines.py扩展
- 利用特性解决问题
定义def open_spider(self,spider)
开始时仅执行一次定义def open_spider(self,spider)
关闭时仅执行一次
item.py
- 预先定义爬取的字段
class TencentItem(scrapy.Item)
title = scrspy.Field()
- 爬虫文件中导入
- from import
- 实例化
item = TencentItem()
item["title"] = xpath获取
- 注:存入数据库中需要转化为字典,
判断item来源,区别来源于哪个爬虫
- if isinstance(item,TencentItem)
setting.py
- 存放公共变量,数据库地址密码等
- from import导入使用
- spider属性settings
- self.settings[“”]
- self.settings.get(“”)
- pipelines中使用
- spider.settings[“”]
- spider.settings.get(“”)
log文件的作用
- scrapy中使用方法
setting.py中设置 LOG_LEVEL = "WARNING"
import loging
logger = logging.getLogger(__name__)
logger.info/warning(item)
item 输出内容
- setting.py设置日志保存本地
LOG_FILE = "./log.log"
- 无框架爬虫文件中使用
- 定义log文件
import logging
logging.basicConfig(样式)
设置输出保存样式logger = logging.getLogger()
__name__ =="__main__"
logger.info/warning(item)
执行文件
- 注:其他文件中可以导入 from log文件名 import logger
实现翻页
- 获取下一页URL地址
- 判断最后一页URL地址
yield scrapy.Request(next_url,callback=self.parse,)
- 如果下一页处理方式不同重新定义方法
- 注 user可以在setting.py中创建
- 注 ()内可以携带参数(method=”“,headers,body,cookie……)
- 参数meta 帮助在不同解析函数传递数据 ,使用场景–列表页获取一部分数据,需要进入详情页获取其他数据,列表的数据传递到获取详情页数据的函数中进行完善。
- meta = {“item”:{“name”:”name”,”old”:”old”…..}}
- response.meta[“item”]
- 数据格式 {“”:”“,”“:”“,}
- item[“”]进行添加详情页数据
- dont_filter = False
- 对于URL地址过滤,防止反复请求,场景:对于数据频繁更新的URL需要反复请求。
Debug的认识
- debug开启会打印settimg中设置的东西
- 打印中间件的内容
- pipline
- 打印访问的url
- 打印爬虫的开始结束
- 各种数据的统计
scrapy shell
scrapy shell +url地址
进入交互式终端- 查看scrapy可用对象
- 查看方法用法response.body ·····查看当前响应
Crawl-spider
- 一般应用于结构简单爬虫,实现自动翻页
scrapy startproject 项目名称
cd 项目名称
scrapy genspider -t crawl 爬虫名称 域名限定
- 类继承自crawlspider
- 爬虫名
- 域名限定
- start_url
- rules 提取数据规则
- 默认生成一个函数,parse函数有特殊定义不能更改
rules = (
#LinkExtractor 连接提取器,提取url地址
#callback 提取出来的url地址的response会交给callback处理
#follow 当前url地址的响应是够重新进过rules来提取url地址,
Rule(LinkExtractor(allow=r'/web/site0/tab5240/info\d+\.htm'), callback='parse_item'),
Rule(LinkExtractor(allow=r'/web/site0/tab5240/module14430/page\d+\.htm'),follow=True),
)局限:规则之间的参数不能传递,适用一级结构翻页,直接获取二级结构内容
- 如果要获取一级结构内容,再获取二级结构内容,rules中翻页callback函数获取一级结构内容,函数中提取二级结构URL,yield发送请求构造函数获取
下载中间件
- middlewares.py
- 每次请求时会经过下载中间件
- setting中开启
- 文件中定义一个中间件类
- def process_request(self,request,spider)
- 可以在数据下载前进行设置处理
- 下载中间件的应用
- 添加自定义useragent
- 添加代理
- 检查代理或者useragent是否可用
scrapy模拟登陆
- 获取cookie,爬取登陆后的页面
- 直接携带cookie登陆
- post请求登陆(默认携带上一次访问的cookie)
- 配合selenium,发送请求前提取本地cookie
- start—url是登陆后的URL地址需要重写start_request方法
- 开启COOKIES_DEBUG=Ture 查看cookies传递的过程
def start_request(self)
cookies = ·····
cookies = {i.split("=")[0]:i.split("=")[1] for i in cookies.split("; ")}#字典推倒式
yield scrapy.Request(
start_url,
callback= ···,
cookies = cookies#注headers不起作用
)data 数据在加载页面中
def parse(self,response)
构造请求数据
post_data = dict(
xxxx=xxxx
)#字典形式
yield scrapy.Formrequest(
登陆地址,
数据,
callback=函数
)可以在登陆后的页面保存HTML查看数据,是否有用户名密码等
用户登陆的form表单有action地址直接输入用户名密码到服务
def parse(self,response)
yield scrapy.FormRequest.from_response(
response,#自动寻找action地址
formdata={},
callback = ····
)
Scrapy_Redis
- scrapy流程
引擎–>调度器获取request对象,存放URL—>下载器发送请求获取数据—>spider爬虫,提取数据,URL,构造request对象—>提取的数据发送给管道/URL构造request发送给引擎发送给调度器
实现多个scrapy下载一个项目
- 多个scrapy把request请求存入Redis数据库,从数据库中获取request对象
Redis去除重复原理
- 每个request对象会生成一个唯一指纹,存入前通过指纹对比避免重复,Redis中存两个数据-待爬数据和爬取过的数据的指纹。
从GitHub下载源码
- clone github scrapy_redis 源码文件
- 研究三个demo
- mv scrapy-redis/example-project ~/scrapyredis_project
- clone github scrapy_redis 源码文件
- 文件作用
- domz.py
- 继承自crowspider爬虫,自动抓取网页
- 名称
- 限定
- 初始URL
- 抓取URL规则
- 函数
pipelines
- item添加键
setting文件
- DUPEFILTER_CLASS去重类
- SCHEDULER调度器类
- SCHEDULER_PERSIST持久化,表示程序结束后内容依然会保持
- ITEM_PIPELINES
- REDIS_URL = “redis://127.0.0.1:6379”
redis 存储的内容
- 爬虫名:request —- 存储待爬取的对象,对象序列化存入Redis,反序列化重新转化为对象
- 爬虫名: items —- 列表,item内容,通过pipeline保存
- 爬虫名:dupefilter —- 集合,存放抓取过的request对象的指纹
源码分析
- pipeline处理数据—判断对象是否存在—存在返回true—hashsha1生成指纹加密返回—实现去重
- 入队问题
- 读scrapy_redis代码
- 源码中redis_pipline 经过处理序列化item转化成字符串,放入键中存入Redis
- 源码中dumpfiler 经过处理request对象,把给定的request对象生成指纹(hashlib.sha1()),注计算指纹不带cookie,headers,不然会导致同样的URL地址生成的指纹不同,重复访问。
- 去重
- 1
f = hashlib.sha1() f.update("xxxx"encoding())
生成指纹 - 2生成指纹放入Redis中实现去重,如果request对象指纹存在返回0,为true,表示之前抓取过
- 源码中scheduler调度器,对是否持久化进行控制,清空待爬对象,清空指纹
- request对象入队问题
- dont_filter过滤+判断指纹是否存在
- 语句if not…and…
- 条件成立不入对
dont_filter false (结果ture) + 指纹存在 true -----return true 不入
dont_filter false (结果true) + 指纹不存在 false -----return false 入队
dont_filter true (结果false) 指纹是否存在 ------ return false 入队
url地址在startURL地址中时候会入队 dont_filter = true
代码练习中发现的问题
pass