Python爬虫
conception
Type
- 通用爬虫
抓取一整张页面内容
- 聚焦爬虫
通用爬虫基础之上,抓取页面特定的局部内容
- 增量式爬虫
检测网站中数据更新的情况,只抓取网站中最新更新出来的数据
反爬机制
门户网站防止爬虫程序获取数据
反反爬机制
破解反爬机制
协议
robots.txt协议
君子协议,规定网站中哪些数据可以被爬虫爬取,哪些不能,没有技术手段
http协议
服务器和客户端进行数据交互的一种形式
常用请求头信息
- User-Agent
请求载体的身份标识
- Connection
请求完毕后,是断开连接还是保持连接
常用响应头信息
Content-Type
服务器响应回客户端的数据类型
https协议
安全(security)的http协议
加密方式称密钥加密
对称密钥加密
非对称密钥加密
证书密钥加密
requests模块
概述
python原生的一款基于网络请求的模块
function
模拟浏览器发送请求
step
指定url -> 发起请求 -> 获取响应数据 -> 持久化存储
import requests #导入requests
# 指定url
url = 'url'
# 进行UA伪装
headers = {'User-Agent':'xxx'}
# 携带参数,post请求为data
params = {}
# 发起get请求,并用response接取返回的对象
response = requests.get(url=url, headers=headers,params=params)
# 获取响应数据
page_text = reponse.text #text(字符串),json(对象),content(二进制)
# 持久化存储
with open('fileName','w',encoding='utf-8') as fp:
fp.write(page_text)
UA
User-Agent(请求载体的身份标识)
UA检测
门户网站的服务器检测载体的身份标识
UA伪装
让爬虫对应的载体身份标识伪装成某一款浏览器
数据解析
聚焦爬虫,爬取页面中指定的页面内容
Theory
进行指定标签的定位
标签或者标签对应的属性中存储的数据值进行提取(解析)
Type
正则
import re
import requests
import os
#创建一个文件夹保存所有图片
if not os.path.exists('./img'):
os.mkdir('./img')
#指定url
url = ''
#UA伪装
headers = {
'User-Agent':''
}
#爬取整张页面
page_text = requests.get(url=url,headers=headers)
#使用聚焦爬虫将页面所有图片进行解析/提取
ex = '<div class="thumb">.*?<img src="(.*?)".*?</div>'
# re.S 单行匹配 re.M 多行匹配
img_src_list = re.findall(ex,page_text,re.S)
for src in img_src_list:
#拼接出一个完整的图片url
src = 'https:'+src
#请求图片的二进制数据
img_data = requests.get(url=src,headers=headers).content
#生成图片名称
img_name = src.split('/')[-1]
#图片的存储路径
imgPath = './img/'+img_name
with open(imgPath,'wb') as fp:
fp.write(img_data)
print(img_name,'over')
bs4(python独有)
实例化一个BeautifulSoup对象,并将页面源码加载到该对象
调用beautifulSoup对象中相关的属性和方法进行数据解析和标签定位
import bs4
#1.将本地的html文档数据加载到该对象中
fp = open(file,'r',encoding = 'utf-8')
soup = BeautifulSoup(fp,'lxml')
#2.从互联网上获取的页面源码加载到该对象
page_text = response.text
soup = BeautifulSoup(page_text,'lxml')
#标签定位
soup.tagName #返回第一个指定标签的所有内容
soup.find(tagName) #同上
soup.find(tagName,class_='') #属性定位
soup.find_all() #用法同上,用列表返回所有符合要求的标签
soup.select(选择器) #可用类选择器,ID选择器等,返回列表
soup.select('.class'>ul a) #层级选择,>一个层级,空格多个层级
#获取标签之间的文本数据
soup.a.text/string/get_text() #string只获取直系内容
#获取标签中的属性值
soup.a['href']
xpath(main)
最常用,最高效,通用性最强的解析方式
- 实例化一个etree对象,且将页面源码数据加载到该对象
- 调用etree对象的xpath方法结合xpath表达式定位标签和内容
from lxml import etree
# 实例化对象
# 1.将本地的html文档数据加载到该对象中
tree = etree.parse(fliePath)
# 2.从互联网上获取的页面源码加载到该对象
page_text = response.text
tree = etree.HTML(page_text)
# 标签定位
r = tree.xpath('/html//div') #返回Element对象列表
# /表示从根节点开始定位,表示一个层级
# //表示多个层级,可以表示从任意位置开始定位
# 属性定位
r = tree.xpath('//div[@class=""]')
# 索引定位,从 1 开始
tree.xpath('//div[@class=""]/p[3]')
# 取文本
/text() #获取直系文本
//text() #获取全部文本
# 取属性
/@attrName
Question
中文乱码的解决
text = text.encode('iso-8859-1').decode('gbk') or result = response.content.decode('utf-8')
登录模拟
爬取某些用户的用户信息
验证码识别
反爬机制
模拟登录需要识别验证码图片中的数据
使用库ddddocr,也可以用pytorch自己写神经网络
import ddddocr
ocr = ddddocr.DdddOcr()
with open('img.png','rb') as img:
img_bytes = img.read()
result = ocr.classification(img_bytes)
print(result)
模拟登录
- 将验证码图片进行本地下载
- 调用验证码处理库进行数据识别
- 发送post请求,处理请求参数
- 响应数据持久化存储
# 获取验证码图片
# 识别验证码
# 发送post请求,模拟登录
# 验证登录是否成功
print(response.status_code)
Cookie
让服务端记录客户端的相关状态
手动Cookie处理
# 抓包获取Cookie值 headers = { 'Cookie':'xxx' }自动处理
使用session会话对象,如果请求过程中产生了Cookie,则会被自动存储/携带在该session对象中
# 创建session对象 session = requests.Session() # 使用session发送post请求 response = session.post(url=url,headers=headers,data=data) # 使用携带cookie的session发送get请求 page_text = session.get().text
代理
破解封IP的反爬机制
代理可以突破自身IP访问的限制,隐藏自身真实IP
requests.get(url=url, headers=headers, proxies={'http':ip_address})
代理类型
协议类型
- http :应用于http协议对应的url
- https :应用于https协议对应的url
匿名度
- 透明:服务器知道该次请求使用了代理,也知道请求对应的真实ip
- 匿名:服务器知道使用了代理,但是不知道真实ip
- 高匿:不知道使用了代理和真实ip
异步
多线程 多进程 :x:
为阻塞的操作单独开启线程或进程,
但不能无限制的开启多线程或多进程
线程池 进程池
降低系统对进程或线程创建销毁的频率,从而降低系统开销
池中线程或进程的数量有上限
# 导入线程池模块的类 from multiprocessing.dummy import Pool # 实例化线程池对象 pool = Pool(4) # 将list每一个元素传递给func进行处理 pool.map(func, list) # pool不在接受新工作 pool.close() # 当所有任务完成后,清空pool pool.join()单线程 + 异步协程 *
# async 修饰函数, 调用后返回协程对象 async def request(url): print('发起请求') return url c = request(url) # 创建一个事件循环对象 loop = asyncio.get_event_loop() # 将协程对象注册到loop, 并且启动loop # loop.run_until_complete(c) # task的使用 task = loop.create_task(c) loop.run_until_complete(task) # 多任务列表 loop.run_until_complete(asyncio.wait(tasks)) # future的使用 task = loop.ensure_future(c) loop.run_until_complete(task) # 绑定回调 def callback_func(task): # result返回 任务对象中封装的协程对象对应的函数的返回值 print(task.result()) # 将回调函数绑定到任务对象 task.add_done_callback(callback_func)
Question
在异步协程中,如果出现同步模块相关的代码,那么就无法实现异步
# 基于同步 response = requests.get(url=url) # 使用aiohttp: 基于异步网络请求模块 async def get_page(url): async with aiohttp.ClientSession() as session: # get() post() # headers,data/params,proxy='http://ip:port' async with session.get(url=url) as response: # text() 返回字符串数据 # read() 返回二进制数据 # json() 返回json对象数据 page_text = await response.text()当在 asyncio 中遇到阻塞操作必须手动挂起
page_text = await response.text()
异步协程的流程
创建异步请求模块
# 调用该方法时返回协程对象 async def get_page(url): async with aiohttp.ClientSession() as session: async with session.get(url=url) as response: page_text = await response.text() # 返回值为任务对象的result() return page_text获取协程对象
c = get_page(url)获取任务对象
task = asyncio.ensure_future(c)为任务对象绑定回调函数
def callback_func(task): print(task.result) # 在task成功完成后,会自动触发回调函数 task.add_done_callback(callback_func)创建事件循环对象,注册任务并启动
loop = asyncio.get_event_loop() loop.run_until_complete(asyncio.wait(tasks))
Selenium
基于浏览器自动化的模块
优点
- 便捷的获取网站中动态加载的数据
- 便捷实现模拟登录
使用流程
环境安装
pip install selenium下载浏览器的驱动程序
实例化一个浏览器对象
from selenium import webdriver bro = webdriver.Edge()编写自动化操作代码
操作代码
让浏览器对url发起请求
bro.get(url)获取浏览器页面源码数据
page_text = bro.page_source标签定位
search_input = bro.find_element_by_id(id)标签交互
search_input.send_keys('xxx')点击事件
button = bro.find_element_by_css_selector('css') button.click()执行js代码
bro.execute_script('js')回退与前进
bro.back() bro.forward()关闭浏览器
bro.quit()切换浏览器标签定位的作用域
# 定位iframe中的标签 bro.switch_to.frame('iframe')
动作链
# 导入动作链对应的类
from selenium.webdriver import ActionChains
# 动作链
action = ActionChains(bro)
# 释放动作链
action.release()
无头浏览器
from selenium.webdriver.edge.options import Options
# 无头浏览器,就是让浏览器在后台运行,不用弹出窗口。
opt = Options()
# 无头浏览器配置
# 使用无头模式
opt.add_argument("--headless")
# 禁用GPU,防止无头模式出现莫名的BUG
opt.add_argument("--disbale-gpu")
# 反检测设置
# 开启开发者模式
opt.add_experimental_option('excludeSwitches', ['enable-automation'])
# 禁用启用Blink运行时的功能
opt.add_argument('--disable-blink-features=AutomationControlled')
# 创建浏览器对象,把参数配置设置到浏览器中
browser = webdriver.Edge(options=opt)
Scrapy框架
项目初始化
创建项目
scrapy startproject projectName进入工程目录
cd projectName在
spiders子目录中创建爬虫文件scrapy genspider spiderName url执行工程
scrapy crawl spiderName
爬虫文件
class NameSpider(scrapy.Spider):
# 爬虫文件的名称,爬虫源文件的唯一标识
name = 'name'
# 允许的域名,用于限定start_urls列表中哪些url可以进行请求发送
allowed_domains = ['www.xx.com']
# 起始的url列表,该列表中的url会自动进行请求发送
start_urls = ['https://www.xx.com/']
# 用作与数据解析,response参数表示请求成功后对应的响应对象
# 该方法调用的次数,为start_urls列表的长度
def parse(self, response):
pass
数据解析
xpath提取内容
# xpath返回列表,且列表元素为 Selector 对象 txt_list = response.xpath() for txt in txt_list: # extract可以将Selector对象中data参数存储的字符串提取出来 print(txt.xpath()[0].extract()) # 列表只有一个元素时,可以使用extract_first,等价于上句 print(txt.xpath().extract_first()) # 列表调用extract,将列表中的每一个Selector对象的data参数提取出来,并返回列表 print(txt.xpath().extract())持久化存储
基于终端命令
只可以将
parse方法的返回值存储到本地文本文件类型只可以为
jsonjsonlinesjlcsvxmlmarshalpicklescrapy crawl spiderName -o filepath.csv基于管道
在
item类中定义相关属性class NameItem(scrapy.Item): field = scrapy.Field()将解析的数据封装存到
item类型对象中# 封装解析的数据 item = NameItem() item['field'] = value # 将item提交给管道 yield item在管道类的
process_item中将接受的item的数据进行持久化存储class NamePipeline(object): fp = None # 重写父类方法,该方法只在爬虫开始时被调用一次 def open_spider(self, spider): # 避免文件被多次打开 self.fp = open(filepath, 'w', encoding) # 该方法只在爬虫结束时被调用一次 def close_spider(self, spider): self.fp.close() # 该方法接收到一个item,调用一次 def process_item(self, item, spider): field = item['field'] self.fp.write(field) return item在配置文件中开启管道
ITEM_PIPELINES = { # 数字表示优先级,数字越小优先级越高 'Name.pipelines.NamePipeline': 300 }
全站数据爬取
将网站某板块下的全部页码对应的页面数据进行爬取
class NameSpider(scrapy.Spider): name = 'name' # 生成一个通用的url模板 url = 'https://www.xx.com/page/%d' page_num = 1 def parse(self, response): txt = response.xpath().extract_first() if(self.page_num < 11): self.page_num += 1 new_url = format(self.url%self.pafe_num) # 手动发送请求,callback回调函数专门用作数据解析 yield scrapy.Request(url=new_url, callback=self.parse)请求传参
# meta参数可以将字典传递给回调函数 scrapy.Request(url=url, callback=self.parse, meta={'item':item})ImagesPipeline
只需要将img的
src提交到管道,管道就会自动发起请求获取二进制数据,且持久化存储- 解析图片地址
- 将存储图片地址的
item递交给管道类 - 管道类中制定一个基于
ImagesPipeline的类- get_media_requests
- file_path
- item_completed
- 配置文件
- 指定图片存储目录
IMAGES_STORE='path' - 开启管道类
- 指定图片存储目录
item = imgItem() item['src'] = src yield itemfrom scrapy.pipelines.images import ImagesPipeline class ImgPipeline(ImagesPipeline): # 对图片地址请求 def get_media_requests(self, item, info): yield scrapy.Request(item['src']) # 指定图片存储路径 def file_path(self, request, response=None, info=None): imgName = request.url.split('/')[-1] return imgName # 将item返回给下一个即将被执行的管道类 def item_completed(self, results, item, info): return item# settings IMAGES_STORE = './imgs'
中间件
下载中间件
file:middlewares.py
拦截整个工程所有的请求和响应
拦截请求
- UA伪装
- 代理IP
拦截相应
- 篡改响应数据
拦截请求
process_request
class NameDownloaderMiddleWare(Object):
# 封装UA池
user_agent_list = []
def process_request(self, request, spider):
# UA伪装
request.headers['User-Agent'] = random.choice(self.user_agent_list)
return None
拦截相应
process_response
拦截异常
process_exception
class NameDownloaderMiddleWare(Object):
# 封装代理ip池
proxy_http = []
proxy_https = []
def process_exception(self, request, exception, spider):
# 代理
if request.url.split(':')[0] == 'http':
request.meta['proxy'] = 'http://'+random.choice(self.proxy_http)
else:
request.meta['proxy'] = 'https://'+random.choice(self.proxy_https)
# 将修正后的请求对象重新发送
return request
CrawlSpier
Spier的一个子类,用于全站数据爬取
创建爬虫文件
scrapy genspier -t crawl xxx www.xxx.com
链接提取器
link = LinkExtactor(allow=r'Items/')根据指定规则,提取指定链接
allow后为正则表达式规则提取器
将链接提取器提取的链接,进行指定规则
callback的解析操作follow
True 可以将链接提取器作用到新提取出的链接中(递归)