爬虫总结

本文详细总结了Python爬虫的基础知识,包括发送请求、获取响应、爬虫分类及流程、HTTP请求头user-agent的作用、requests库的使用、数据提取、Python2与Python3的字符串差异、HTTP状态响应码等。还深入讲解了模拟登录、处理SSL证书、正则表达式、XPath、Scrapy框架、Selenium自动化测试、MongoDB数据库操作、Scrapy Redis分布式爬虫、模拟登陆策略等高级话题,涵盖了爬虫开发的各个方面。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

爬虫总结

day01
1.什么是爬虫?
爬虫就是:模拟浏览器发送请求,获取响应

2.爬虫的分类,爬虫的流程
聚焦爬虫:针对特定的网站的爬虫
准备url地址 -->发送请求 获取响应–> 提取数据–> 保存
获取响应–> 提取url地址,继续请求
通用爬虫:搜索引擎的爬虫
抓取网页–> 数据存储–>预处理–> 提供检索服务,网站排名

3.浏览器发送请求的过程
爬虫请求的:url地址对应的响应
浏览器获取到的内容:elements的内容=url对应的响应+js+css+图片
爬虫获取的内容和elements内容不一样,进行数据提取的时候,需要根据url地址对应的响应为准进行数据的提取

4.http的请求头user-agent有什么用
user-agent:告诉对方服务器是什么客户端正在请求资源,爬虫中模拟浏览器非常重要的一个手段
爬虫中通过把user-agent设置为浏览器的user-agent,能够达到模拟浏览器的效果
cookie:获取登录只有才能够访问的资源
5.利用requests模板如何发送请求和获取响应

response = requests.get(url)
response.text -> str # 根据响应信息进行有规律的推测网页的编码
response.encoding=“utf-8”
response.encoding=”gbk”
response.content -> bytes
response.content.decode(“utf8”)

发送请求,获取响应

def parse(self, url, data):
response = requests.get(url,params=params,headers=self.headers)
response = requests.post(url,data=data, headers=self.headers)
return response.content.decode()
response.status_code
response.request.headers
response.headers

一般来说名词,往往都是对象的属性,对应的动词是对象的方法
获取网页源码的通用方式:

response.content.decode() 的方式获取响应的html页面
response.content.decode(“GBK”) response.text

6.python2和python3中的字符串

ascii 一个字节表示一个字符
unicode 两个字节表示一个字符
utf-8 边长的编码方式,1,2,3字节表示一个字符

python2
字节类型:str,字节类型,通过decode()转化为unicode类型
unicode类型:unicode ,通过encode转化为str字节类型
python3
str:字符串类型,通过encode() 转化为bytes
bytes:字节类型,通过decode()转化为str类型

7.常见的状态响应码
200:成功
302:临时转移至新的url
307:临时转移至新的url
404:not found
500:服务器内部错误

day02
1.requests中headers如何使用,如何发送带headers的请求
模拟浏览器,欺骗服务器,获取和浏览器一致的内容

headers = {“User-Agent”:“从浏览器中复制”}
headers = {
“Origin”: “http://ntlias-stu.boxuegu.com”,
“Referer”: “http://ntlias-stu.boxuegu.com/”,
“User-Agent”: “Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.17 Safari/537.36”
}
requests.get(url,headers=headers)

2.发送带参数的请求

params = {"":""}
url_temp = “不完整的URL地址”
requests.get(url_temp,params=params)

3.requests如何发送post请求

data = {“从浏览器中form data的位置寻找”}
requests.post(url,data=data)

4.requests中如何使用代理,使用代理的目的,代理的分类

 proxies = {“https”: “https://117.127.0.195:8080”}
    proxies = {协议:协议+ip+端口}
    requests.get(url,proxies=proxies)

目的:
反反爬
隐藏真实ip
代理的分类

高匿名代理:不知道在使用代理
匿名代理:知道在使用代理,但是不知道真实ip
透明代理(Transparent Proxy):对方知道真实的ip

5.requests中session类如何使用,为什么要使用session

session = requests.Session()
session.post(url,data) #cookie会保存在session中
session.get(url) #用session发送请求会带上之前的cookie
注意:这块的session类和之前所学的session无任何关系

6.列表推导式

1、In [41]: [i for i in range(10)]
2、Out[41]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
3、In [42]: [i/2 for i in range(10)]
4、Out[42]: [0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5]
5、In [43]: [i/2 for i in range(10) if i%2==0]

day03
1.requests进行携带cookie登录

cookie字符串放在headers中
把cookie字典交给requests请求方法的cookies

2.寻找登录接口的方法
form表单action对应的url地址
用户名和密码的input标签中,name的值作为键,用户名和密码作为值的字典,作为post data
通过抓包,定位url地址
form data

3.分析js,获取加密的数据
观察变化
定位js
通过event listener定位js的位置
通过搜索url地址中的关键字,通过chrome的search all file来进行搜索
进行分析
通过添加断点的方式分析js
执行js
完全的使用python模拟js的执行过程

4.requests处理ssl证书
requests.get(url,verify=False)

5.获取响应中的cookie,转化为字典

response = requests.get(url,headers=headers)
requests.utils.dict_from_cookiejar(response.cookies)

6.requests中超时参数的使用,retrying模块的使用

from retrying import retry
requests.get(url,timeout=3)
通过装饰器的方式使用retry,进行异常捕获,重新执行被装饰的函数
from retrying import retry
@retry(stop_max_attempt_number=3)
def fun():
pass

7.数据的分类
结构化数据 json, xml
直接使用模块转化为python类型
非结构化数据 html
re,xpath

8.json模块的使用

数据交换格式 json.loads(json_str) json字符串转化为python类型
json.dumps(python_type,ensure_ascii=False,indent=2) python类型转化为json字符串
json.load() 把包含json的类文件对象中的数据提取出来转化为python类型 json.dump()
python类型存入类文件对象中 那么对于为什么需要模拟登陆?
获取cookie,能够爬取登陆后的页面

9.requests模拟登陆的三种方法

session
实例化对象
session.get(url) #cookie保存在session中
session.get(url) #带上保存在session中cookie
cookie方法headers中
cookie传递给cookies参数
cookie = {“cookie 的name的值”:“cookie 的value对应的值”}

10.三元运算符
a = 10 if 3<2 else 100

11.字典推导式

In [8]: {i:i+10 for i in range(10)}
Out[8]: {0: 10, 1: 11, 2: 12, 3: 13, 4: 14, 5: 15, 6: 16, 7: 17, 8: 18, 9: 19}
In [9]: {i:i+10 for i in range(10) if i%2==0}
Out[9]: {0: 10, 2: 12, 4: 14, 6: 16, 8: 18}
day04
1.正则的语法

字符
. 能够匹配\n之外的所有字符 re.S模式下可以匹配\n
\ 转义
[] 或的效果,从中选择一个, [abc]+ 能够匹配多个
| 或的效果
预定义的字符集
\d 数字
\s 空白字符串,包含空格、\n,\t
\w 单词字符,a-zA-Z0-9_
数量词
匹配0次或者多次
能够匹配1次或者多次
? 能够匹配0-1次,让*,+非贪婪

2.re模块的常用方法
re.findall(“正则表达式regex”,“待匹配的字符串”) # 返回列表,或者是空列表
re.sub(“regex”,"",“待替换的字符串”) # 返回字符串
p = re.compile(“regex”,re.S/re.DOTALL) # 返回一个p模型,编译,提高匹配效率
p.findall(“待匹配的字符串”)
p.sub("
",“待替换的字符串”)
注:re.S 匹配\n re.DOTALL 匹配Tab键

3.原始字符串r
定义:相对于特殊符号而言,表示特殊符号的字面意思
用途
正则中,能够忽略转义符号带来的影响,待匹配的字符串中有几个\,正则表达式中加上r,照着几个\即可
windows文件路径
4.xpath语法

xpath的安装 pip install lxml
// 的用途
//a html中所有的a
div//a div中所有的a,包括div下的后代节点中的a
a//text() a下的所有的文本
@ 的使用
a/@href 获取a的href的值
//a[@class=‘b’]
text() 的使用
//a/text() 获取所有的a下的文本
//a[text()=‘下一页’] 获取文本为下一页的a标签
a//text() a下的所有的文本
xpath包含的语法
//div[contains(@class,“i”)] class包含i的div标签
//a[contains(text(),“下一页”)] 文本包含下一页的a标签
兄弟标签
/a/follow-sibling::ul[1]
xpath 选择特定位置
//a[1] 第一个
//a[last()] 最后一个
//a[last()-1] 倒数第二个
//a[postion()<4] 前三个

5.lxml模块的使用

from lxml import etree
element = etree.HTML(bytes/str) #返回element
ret_list = element.xpath("xpath字符串") #返回列表
bytes = etree.tostring(element) #返回bytes类型字符串
#数据提取时:先分组,再提取

day05
1.xpath包含的语法

//div[contains(@class,“i”)] class包含i的div标签
//a[contains(text(),“下一页”)] 文本包含下一页的a标签

2.url地址解码的方法
requests.utils.unquote(url)

3.准备url地址
知道url地址的规律,知道一共多少页,准备url列表,果壳,糗百
不知道url地址规律,或者不知道一共多少页,准备start_url ,贴吧

4.多线程爬虫

threading
t1 = threading.Thread(targe=func,args=(,))
t1.setDaemon(True) #设置为守护线程
t1.start() #此时线程才会启动
队列
from queue import Queue
q = Queue()
q.put() 队列计数+1
q.get() 队列计数不会-1
q.task_done() 和get()方法配合,队列计数-1
q.join() #阻塞主线程,让主线程等待队列任务结束之后在结束,队列任务在计数为0时技术

5.多进程爬虫
multiprocessing
p = multiprocessing.Process(trage=func,args=(,))
p.daemon = True #设置为守护进程,主线程结束,子进程结束
p.start()
from multiprocessing import JoinableQueue
q = JoinableQueue()
q.join() # 让主进程阻塞,等待队列任务计数,计数为0队列任务结束,
q.put() # 计数+1
q.get() # 计数不会-1
q.task_done() # get和task_done一起使用才会减一

6.线程池和协程池的使用
线程池

from multiprocessing.dummy import Pool
pool = Pool(5)
pool.apply_async(func,callback=func2)
协程池
import gevent.monkey
gevent.monkey.patch_all()
from gevent.pool import Pool
pool = Pool(5)
pool.apply_async(func,callback=func2)

day06
1.安装driver

chromdriver 需要对应chrome版本
提示权限不足,sudo chmod +x phantomjs
chromdriver --version
phantomjs --version

2.selenium如何使用
功能:请求页面,提取数据,开启隐形的浏览器,能够执行其中的js,可获取cookie

from selenium import webdriver
driver = webdriver.PhantomJS() # 没有界面,不建议使用
driver = webdriver.Chrome() # 带界面
driver.get_cookie(‘name’) # 获取cookie值,需要传name
driver.get_cookies() # 获取cookie
driver.get(url) #发送请求
driver.quit()

3.selenium定位元素的方法
driver.find_element #返回第一个元素,如果没有报错
driver.find_elements 返回包含元素的列表,没有就是空列表
driver.find_elements_by_xpath() # 只能定位到标签(即元素), 不能定位到文本值和属性值
driver.find_elements_class_name() #定位class属性
driver.find_elements_by_id() #定位id属性
driver.element.text #获取文本
driver.element.get_attribute(“textContent”) #获取隐藏元素的文本
driver.element.get_attribute(“href”) #元素获取属性值

4.selenium如何处理frame
driver.switch_to.frame(id,name,element)

5.验证码的识别
url地址不变,验证码不变
请求验证码的地址,获取响应,进行识别
url地址不变,验证码变化
请求验证码,发送登录请求,需要带上统一套cookie,才能够都能路成功,对应可以使用requests.Session()来实现
selenium处理验证码
带上selenium的driver中的cookie来请求验证码
selenium截屏,获取验证

6.mongodb的服务端和客户端启动方法
服务端启动
sudo service mongod start
sudo mongod --config /ect/mongod.conf &
客户端启动
mongo

7.mongodb中数据库的方法
数据库可以不需要提前创建,使用use一个不存在的数据库即可创建
use db_name 使用数据库 数据库可以不存在
db 查看当前所在的数据库
show dbs /show databases 查看所有的数据库
db.dropDatabase() 删除数据库
数据库名.dropDatabase() #删除数据库

8.mongodb中集合的方法
集合不需要提前创建,插入数据的时候自动创建
show collections #查看所有的集合
db.集合名.drop() #删除集合
db.集合名.find() #集合的使用

9.mongodb的增删改查的方法
插入insert
insert() 插入数据,_id相同会报错
save() 保存数据_id相同会更新,不存在会插入
删除remove
db.col_name.remove({条件},{justOne:flase}) #默认删除全部满足条件的内容
db.col_name.remove({条件},{justOne:ture}) 删除一条满足条件的内容
更新update
update 更改时,找到满足条件时,除id之外全部覆盖
db.collection.update({条件},{KaTeX parse error: Expected 'EOF', got '}' at position 17: …et:{name:10086}}̲,{multi:true}) …set:{name:1}})
把满足条件的第一条的name值更新成1
db.col_name.update({条件},{$set:{name:1}},{multi:true})

day07
1.mongodb在pycharm中的增删改查

stu.insert({"name": "张三", "age": 12})  # 增
stu.remove({"age": 12}, multi=True)    # 删
stu.update({"age": 12}, {"$set": {"name": "李斯"}}, multi=True)  # 改
for data in stu.find():  # 查
     print(data)

2.mongodb的运算符

  • 比较运算符
    • $gt 大于
    • $lt 小于
    • $gte 大于等于
    • $lte 小于等于
    • $ne 不等
  • 逻辑运算符
    • and {age:"",name:""}
    • or {$or:[{条件1},{条件2}]}
  • 范围运算符
    • KaTeX parse error: Expected '}', got 'EOF' at end of input: …col.find({age:{in:[18,19,30]}})
    • $nin 不在范围内

3.mongodb中的计数,去重,排序

- 计数
  - db.col.count({条件})
  - db.col.find({条件}).count()
- 去重
  - db.col.distinct("字段",{条件})
- 排序
  - db.col.find().sort({})
- 投影
  - 指定数据内容的字段
  - db.stu.find({条件},{name:1,_id:0})
  - 返回的数据中只会包含name字段,_id不会显示

4.mongodb聚合中$group的使用

分组
db.stu.aggregate(
{KaTeX parse error: Expected ‘}’, got ‘EOF’ at end of input: group:{_id:“hometown”,count:{KaTeX parse error: Expected ‘EOF’, got ‘}’ at position 6: sum:1}̲,total_age:{sum:“KaTeX parse error: Expected ‘EOF’, got ‘}’ at position 5: age"}̲,avg_age:{avg:”$age"}}}
)

_id分组的依据

$age 取age对应的值

$sum:1 把每条数据作为1进行统计,统计的是个数

sum:" sum:"sum:“age” 统计年龄对应的和

$group对应的字典中的键是输出数据的键

不分组
db.stu.aggregate(
{KaTeX parse error: Expected ‘}’, got ‘EOF’ at end of input: …id:null,count:{sum:1}}}
)

按照一个字段分组

db.col.aggregate(
{KaTeX parse error: Expected '}', got 'EOF' at end of input: group:{_id:"gender",count:{KaTeX parse error: Expected 'EOF', got '}' at position 6: sum:1}̲}} ) 按照多个…group:{_id:{gender:“gender&quot;,hometown:&quot; gender&quot;,hometown:&quot;gender",hometown:"hometown”},count:{KaTeX parse error: Expected 'EOF', got '}' at position 6: sum:1}̲}} ) 不分组,…group:{_id:null,count:{$sum:1}}}
)
KaTeX parse error: Expected '}', got 'EOF' at end of input: …ggregate( {group:{_id:“KaTeX parse error: Expected '}', got 'EOF' at end of input: gender",name:{push:”$name"}}}
)

4.统计整个文档
数据透视
把不同行的数据,放到一行来展示

db.stu.aggregate(
{KaTeX parse error: Expected '}', got 'EOF' at end of input: group:{_id:"gender",name:{push:&quot; push:&quot;push:"name"},hometown:{push:&quot; push:&quot;push:"hometown"}}}
)

按照多个字段进行分组
按照多个字端进行分组,_id的值是一个json
db.stu.aggregate(
{KaTeX parse error: Expected ‘}’, got ‘EOF’ at end of input: …_id:{hometown:“hometown”,gender:“KaTeX parse error: Expected ‘EOF’, got ‘}’ at position 8: gender”}̲,count:{sum:1}}}
)

多字段分组练习
当某个键对应的值是字典的时候,取其中的值需要使用.操作,$_id.country表示取到_id这个字典下的country的键对应的值

第一条作为第二条的管道进行查找,计数

db.tv3.aggregate(
{KaTeX parse error: Expected ‘}’, got ‘EOF’ at end of input: …{_id:{country:“country”,province:“province",userid:" province",userid:“province”,userid:“userid”}}},
{KaTeX parse error: Expected ‘}’, got ‘EOF’ at end of input: …{_id:{country:”_id.country",province:“KaTeX parse error: Expected ‘EOF’, got ‘}’ at position 14: _id.province”}̲,count:{sum:1}}}
)

5.mongodb中$match
过滤

db.col.aggregate(
  {$match:{age:{$gt:18}}}
    )

6.mongodb中$project

投影,修改文档的输入输出结构

db.stu.aggregate(
  {$group:{_id:"$hometown",count:{$sum:1}}},
  {$project:{_id:0,sum:"$count",hometown:"$_id"}}
  )
db.tv3.aggregate(
  {$group:{_id:{country:"$country",province:"$province",userid:"$userid"}}},
  {$group:{_id:{country:"$_id.country",province:"$_id.province"},count:{$sum:1}}},
  {$project:{country:"$_id.country",province:"$_id.province",counter:"$count",_id:0}}

)
7.limit $sort

db.stu.aggregate(
  {$group:{_id:"$hometown",count:{$sum:1}}},
  {$sort:{count:-1}},
  {$skip:1},
  {$limit:2}
  )

8.mongodb索引
创建索引
db.col.ensureIndex({name:1})
db.col.createIndex()
查看索引
db.col.getIndexes()
删除索引
db.col.dropIndex({name:1})
建立联合索引
db.col.ensureIndex({name:1,age:-1})
建立唯一索引
db.col.ensureIndex({name:1},{unique:true})

9.mongodb备份和恢复
备份
mongodump -h host -d database -o output_path
恢复
mongorestore -h host -d database --dir 恢复的路径

10.pymongo的使用

from pymongo import MongoClient

#实例化client
client = MongoClient(host,port)  
#选择集合
collection = client["db"]["collection"]

#查询
collection.find() #返回全部的数据,返回cursor对象,只能获取其中内容一次
collection.find_one() #返回一条

#插入
collection.isnert_one()
collection.insert_many()

#更新
collection.update_one({name:"a"},{"$set":{"name":"noob"}})
collection.update_many()

#删除
collection.delete_one()
collection.delete_many()

day08
1.scrapy框架安装
pip install Twirted.whl(本地)
pip install pywin32
pip install scrapy

2.scrapy 的数据传递的流程
五大组件
调度器:存储请求队列
下载器:根据request发情请求获得响应response
爬虫:提取url转为request,提取数据
管道:数据清洗和数据保存
引擎:负责连接其他四个组件(之间互不相通),保证数据的传递
中间件 只能处理request和response
爬虫中间件
下载器中间件
运行过程
0.调用start_requests()方法,将start_urls中所有的url构造成request对象,并放入调度器
1.调度器取一个request -> 引擎 -> 下载器中间件 -> 下载器
2.下载器根据request下载得到response -> 下载器中间件 -> 引擎 -> 爬虫中间件 -> 爬虫
3.爬虫提取数据
3.1 爬虫提取url转为request -> 爬虫中间件 -> 引擎 -> 调度器
3.2 爬虫提取数据(item) -> 引擎 -> 管道
4.管道实现数据的处理和保存
3.scrapy爬虫项目的创建
创建项目
scrapy startproject myspider
创建爬虫
cd myspider
scrapy genspider spider_name allowed_domain
运行
scrapy crawl spider_name
构造一个request请求
yield scrapy.Request(url,callback,meta,dont_filter) # url需要手动补全
callback: 将来url响应的处理函数
meta:数据不完整时,传递当前数据到下一个响应函数
dont_filter:默认False,即过滤,过滤的情况下,不会重复发起相同的url请求
response.follow(url) # url不用补全,会根据response.url自动补全
完善爬虫 :提取数据,提取url地址组成request
完善管道:数据的处理和保存
yield 能够yield None,item对象,字典,或者请求,不能够yield一个列表

4.完善spider
parse方法必须有,用来处理start_urls对应的响应的
extract() response.xpath() 从中提取数据的方法,没有就返回一个空列表
extract_first() response.xpath() 的结果中提取第一个字符串的方法,没有返回None值

5.完善管道
管道需要在settings中开启,添加管道的路径,对应的键:管道的位置,值表示的是管道距离引擎的远近,数字越小,优先级越高,越先经过

process_item(item,spider)方法必须有 ,spider表示的是传递item过来的爬虫实例

从爬虫中通过yield 把数据交给引擎传递给pipeline,只能是Request, BaseItem, dict or None

class YangguangPipeline(object):
def process_item(self, item, spider):
处理从spider发来的item数据

需要在配置文件中开启后才能生效
ITEM_PIPELINES = {
‘yangguang.pipelines.YangguangPipeline’: 300, # 300标识数据处理的优先级,数字越低,优先级越高}

6.数据提取url地址补全
手动字符串相加
urllib.parse.urljoin(baseurl,url)
后面的url会根据baseurl进行url地址的拼接
import urllib
url1 = “position.php?&start=2890#a”
url2 = “https://hr.tencent.com/position.php?&start=3580#a”
urllib.parse.urljoin(url2, url1)
输出:“https://hr.tencent.com/position.php?&start=2890#a”
next_url = urllib.parse.urljoin(response.url, next_url) # response.url可以获取完整的url字符串,是response的一个属性
response.follow(url,callback)
能够根据response的地址把url拼接完整,构造成Request对象请求
7.scrapy如何构造请求
scrapy.Request(url,callback,meta,dont_filter)
url:详情页,下一页的url
callback:url地址响应的处理函数
meta:在不同的函数中传递数据
dont_filter ::默认是false表示过滤,scrapy请求过的url地址,在当前的运行过程中不会继续被请求,如果需要继续被请求,可以把dont_filter=True
yield scrapy.Request(url,callback,meta,dont_filter)

8.scrapy的Item如何使用
#定义

class Item(scrapy.Item):
  name = scrapy.Field()  

#使用
导入。使用name字典

9.scrapy中parse函数是做什么的

处理start_urls中的url地址的响应

day09

1.scrapy shell 如何使用,能干什么
scrapy shell url 能够进入交互式终端
查看scrapy中模块的属性
测试xpath
2.response对象有哪些常见属性

response.body 能够后去响应bytes字符串
response.url
response.request.url
response.headers
response.request.headers
resposne.text 能够后去响应str字符串

3.open_spider 和close_spider 在管道里面配置
open_spider(spider) #能够在爬虫开启的时候执行一次
close_spdier(spider) #能够在爬虫关闭的时候执行一次
在和数据库建立连接和断开连接的时候使用上述方法

4.deepcopy的使用,苏宁代码中为什么需要deepcopy
a = deepcopy(b) #强制传值
苏宁代码数据重复
scrapy中的内容是异步执行的,解析函数可能同时在执行,操作的同一个item,
大分类下的所有的图书用的是一个item字典

day10
1.crwalspider如何创建爬虫

scrapy genspider -t crawl 爬虫名 语序允许爬取的范围

2.crwalspdier中rules的编写
rules 元组,元素是Rule
Rule(LinkExtractor(allow=“正则”),follow=True,callback=“str”)
LinkExtractor:传入正则匹配url地址
follow:为True表示提取出来的响应还会经过rules中的规则进行url地址的提取
callback:表示提取出来的响应还会经过callback处理

3.crwalspider中不同的解析函数间如何传递数据,如果不能,应该如何操作?

在前一个Rule的callback中实现手动构造请求

yield scrapy.Request(url,callback,meta)

4.下载器中间件如何使用
使用代理

request.meta[‘proxy’] = “协议+ip+端口”

在settings中开启中间件
SPIDER_MIDDLEWARES = {
‘book.middlewares.BookSpiderMiddleware’: 543,
}
DOWNLOADER_MIDDLEWARES = {
‘book.middlewares.BookDownloaderMiddleware’: 543,
}

class TestMid:
def process_request(self,request,spdier):
#处理请求
request.headers[“User-Agent”] = “” #使用ua
request.meta[“proxy”] = “协议+ip+端口”
return None #请求会继续后面处理
return Request #把请求交给调度器
return Response #把响应交给爬虫

def process_response(self,request,response,spider):
  #处理响应
  return response #把响应交给爬虫
  return request #把请求交给调度器

5.模拟登陆的三种方式

携带cookie进行登录
yield scrapy.Request(url,callback=self.parse,cookies={},meta={})
发送post请求
yield scrapy.FormReuqest(url,formdata={请求体},callback=self.parse_login)
表单提交
yield scrapy.FormReuqest.from_response(response,formdata={},callback=self.parse_login)

6.正则比倒是忽略大小写
re.I/re.IGNORECASE

day11
scrapy_redis
scrapy_redis 是scrapy框架的一个扩展组件,实现了两个功能:

增量式爬虫 分布式爬虫
实质:就是将请求队列和指纹集合进行了持久化存储
在seeeting.py中继续配置

# 指定了去重的类
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
# 制定了调度器的类
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
# 调度器的内容是否持久化
SCHEDULER_PERSIST = True
REDIS_URL = "redis://127.0.0.1:6379"

注:scrapy_redis 只是scrapy框架的一个组件

2.scrapy_redis为什么能够实现去重和请求的持久化以及分布式爬虫

scrapy_redis把指纹和request对象存储在了redis,下一次程序启动起来之后会从之前的redis中读取数据
实现分布式:过个服务器上的爬虫公用一个redis

3.scray_redis中dmoz给我们展示了一个什么样的爬虫
增量式的爬虫:基于request对象的增量
分布式爬虫
主要体现在 请求队列和指纹集合的持久化存储

4.scrapy_redis中如何实现一个增量式的爬虫
settings中进行配置,指定去重的类,调度器的类,添加上redis_url
DUPEFILTER_CLASS = “scrapy_redis.dupefilter.RFPDupeFilter” # 指定去重的类
SCHEDULER = “scrapy_redis.scheduler.Scheduler” # 调度器的类
SCHEDULER_PERSIST = True # 指定调度器的内容是否持久化
REDIS_URL = “redis://127.0.0.1:6379” # 添加redis_url地址

5.scrapy中如何生成的指纹,有什么启发

使用sha1加密请求的url地址,请求的方法和请求体,得到16进制字符串作为指纹 每个指纹40位

fp = hashlib.sha1()   # sha1加密
fp.update(to_bytes(request.method))  # 请求方法
fp.update(to_bytes(canonicalize_url(request.url)))  # 请求地址
fp.update(request.body or b'')  # 请求体

cache[include_headers] = fp.hexdigest()  #生成指纹  加密之后的16进制字符串

6.scrapy_redis什么情况下request会入队

def enqueue_request(self, request):
    if not request.dont_filter and self.df.request_seen(request):
        self.df.log(request, self.spider)
        return False
    self.queue.push(request)
    return True

全新的request对象,之前没有见过的request
dont_filter = True 不过滤,请求过的url地址让他继续请求
start_urls中的url地址能够反复入队请求,因为默认是不过滤的

7.要查看数据是否存在于redis的集合中,如果不存在就插入

added = self.server.sadd(self.key, fp)
#added= 0 表示存在
#added!=0 表示不存在,并且已经插入
return added == 0

8.关于对象的序列化和反序列化

request = Request(“http://www.baidu.com”, meta={“item”: {“hello”: “wrold”}}, parse=“parse_book”)
1
对象的序列化
data = pickle.dumps(request)
print(data)
反序列化
req = pickle.loads(data)
print(req.meta)
print(id(request), id(req))
如何去重
1.请求生成指纹

fp = hashlib.sha1()
fp.update(to_bytes(request.method))
fp.update(to_bytes(canonicalize_url(request.url)))
fp.update(request.body or b'')
return fp.hexdigest()

利用hashlib的sha1,对request的请求体、请求url、请求方法进行加密,返回一个40位长度的16进制的字符串,称为指纹

进队

def enqueue_request(self, request):
if not request.dont_filter and self.df.request_seen(request):
self.df.log(request, self.spider)
return False
self.queue.push(request)
return True

如果请求需要过滤,并且当前请求的指纹已经在指纹集合中存在了,就不能进入队列了

如果不需要过滤,直接进入队列

如果请求需要过滤,并且请求的指纹是一个新的指纹,进入队列

10.数据去重

中间件去重
process_response(request,response,spider):
#set可以是内存set集合,也可以是redis的set
ret = set.add(md5(response.body))
if ret == 0:
return request
else
return response
建立复合索引

# 复合索引,加速和去重
stu.ensure_index([(“hometown”, 1), (“age”, 1)], unique=True)
# 根据数据的特征,在mongodb中 对指定字段建立复合索引,所有字段值相同时就无法二次插入了

布隆过滤器

11.哈希函数的特性:

输入域无限,输出域有限
相同的输入 必然 得到 相同的输出 (不是随机性)
不同的输入 也可能 得到 相同的输出 (哈希碰撞)
离散性,对于输出域中的每个结果,在整个输出域是均分分布的。
dict 字典 -> 哈希表

day12

1.Redisspider的爬虫和scrapy.spider的区别

实现Redisspider 分布式爬虫,请求的持久化,去重的持久化

区别
父类不一样,RedisSpider继承的父类是RedisSpider
RedisSpider没有start_url, 多了redis_key ,往redis_key存入start_url地址
redis_key表示redis中存放start_url地址的键
settings 中多了几行配置

创建爬虫
scrapy genspider 爬虫名 爬取范围
修改父类名
修改redis_key
启动爬虫
让爬虫就绪:scrapy crawl 爬虫名
redis中存入url地址:lpush redis_key url

2.RedisCrawlSpider的爬虫和crwalspdier的区别
实现RedisCrawlSpider 分布式爬虫,请求的持久化,去重的持久化

区别
RedisCrawlSpider继承的父类是RedisCrawlSpider
RedisCrawlSpider没有start_url,多了redis_key ,往redis_key存入start_url地址
redis_key表示redis中存放start_url地址的键
settings 中多了几行配置

创建爬虫
scrapy genspider -t crawl 爬虫名 允许爬取的范围
修改父类名
添加redis_key
启动爬虫
让爬虫就绪 scrapy crawl 爬虫
lpush redis_key url 爬虫会启动

3.crontab使用的方法

分钟 小时 日 月 星期 命令
30 9 8 * * ls #每个月的8号的9:30执行ls命令
在爬虫中使用crontab

爬虫启动命令写入脚本文件

cd dirname $0
scrapy crawl 爬虫名 >> run.log 2>&1

给脚本添加可执行权限
chmod +x run.sh
把脚本文件添加到crontab的配置中
30 6 * * * /home/python/myspider/run.sh

day13
1.什么是框架,为什么需要开发框架

框架:为了解决一类问题而开发的程序,能够提高开发效率
第三方的框架不能够满足需求,在特定场景下使用,能够满足特定需求

2.scrapy_plus中有哪些内置对象和核心模块

core
engine
scheduler
downloader
pipeline
spider
http
request
response
middlewares
downloader_middlewares
spider_middlewares
item

3.说出scrapy_plus实现引擎的基础逻辑
1.调用爬虫的start_request方法,获取start_request请求对象
调用爬虫中间件的process_request方法,传入start-request,返回start_request
2.调用调度器的add_request,传入start_request
3.调用调度器的get_request方法,获取请求
调用下载器中间件的process_request,传入请求,返回请求
4.调用下载器的get_response方法,传入请求,返回response
调用下载器中间件的process_response方法,传入response,返回response
调用爬虫中间件的process_response方法,传入response,返回response
5.调用spider的parse方法,传入resposne,得到结果
调用爬虫中间件的process_request方法,传入request,返回request
6.判断结果的类型,如果是请求对象,调用调度器的add_request,传入请求对象
7.否则调用管道的process_item方法,传入结果
4.如何在项目文件中添加配置文件能够覆盖父类的默认配置

1.在框架中conf文件夹下,建立default_settings,设置默认配置
2.在框架的conf文件夹下,建立settings文件,导入default_settings中的配置
3.在项目的文件夹下,创建settings文件,设置用户配置
4.在框架的conf文件夹下的settings中,导入settings中的配置,会覆盖框架中的默认配置

day14
1.getattr如何使用?

getattr 通过传入字符串,获取python对象或者是方法
现在有test(),知道test中间有个方法名叫做func
getattr(test(),“func”) #返回test.func()
2.importlib如何使用?

能够动态的导入模块

import importlib
module = importlib.import_module(“模块的位置”)
cls = getattr(module,“Test”) #获取模块下的类
func = getattr(cls(),“func”) #获取cls中的func方法

爬虫项目

运行环境

linux+pycharm+redis+scrapy+mysql+mongodb+scrapy_redsi+selenium

项目描述
抓取了(多个)网站,获取了数据,解决了***需求,使用的技能
request+selenium
selenium + scrapy
scrapy_redsi
自己实现的框架完成了一个项目

个人职责
使用的是技能
request :发送请求
selenium
获取动态html页面,数据提取更方便
专门进行登录,获取cookie,组成cookie池,其他程序从cookie池中获取cookie,请求登陆之后的页
scrapy
为了加快抓取速度,使用了scrapy
scrapy_redis
为了试下增量式爬虫,使用了scrapy_redis
为了实现分布式,使用了scrapy_redis
为了实现持久化的去重,
自己实现的框架完成了一个项目
框架的实现逻辑
实现去重的方式
分布式的实现方式
持久化的实现

反扒
js生成的数据
使用selenium配合无头浏览器
分析了js,看到了js的实现过程,python实现了一遍
js2py的工具执行了js

验证码
使用了打码平台

代理ip
对方服务器有通过ip进行限速,购买了代理ip组成了ip池,通过一个程序,判断ip的可用性

去重
基于url地址去重
sha1加密了以写请求的字典
基于数据的去重
sha1加密了数据中的某些字典,得到指纹,存在redis中的集合中进行对数据的去重
在数据库中建立联合索引进行去重

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值