爬虫相关知识点
- 什么是爬虫?
- 爬虫与web后端服务之间的关系
- Python爬虫技术的相关库
- 常见反爬虫策略
- 爬虫库urlib [重点]
一、什么是爬虫?
1.1 爬虫spider的概念
爬虫用于爬取数据,又称为数据采集程序。
爬取的数据来源于网络,网络中的数据可以是由web服务器(Nginx/Apache)、数据库服务器(mysql、Redis)、索引库 (ElastichSearch)、大数据(Hbase/Hive)、视频/图片库(FTP)、云存储(阿里云的OSS)等提供的。
爬虫的数据是公开的,非盈利目的。
1.2 Python爬虫
使用Python编写的爬虫脚本(程序)可以完成定时、定量、指定目标(web站点)的数据爬取。主要使用多(单)线程/进程、网站请求库、数据解析、数据存储、任务调度等相关技术。
Python爬虫工程师,可以完成接口测试、功能接口测试、性能测试和集成测试。
二、爬虫与web后端服务之间的关系
爬虫使用网络请求库,相当于客户端请求,web后端服务器根据请求响应
数据。
爬虫向服务器发起http请求,正确的接受响应数据,然后根据数据的
类型(Content-Type)进行数据的解析及存储。
爬虫程序发起请求前,需要伪造浏览器(User-Agent指定请求头)
然后再向服务器发起请求,响应200的成功率高很多。
http报文格式
三、Python爬虫技术的相关库
网络请求:
- urlib(urllib2和urllib3)
- requests
- selenium(UI自动测试,动态json渲染)
- appium(手机app应用的爬虫或者UI测试)
数据解析:
- re正则
- xpath
- bs4
- json
数据存储:
- pymysql
- mongodb
- elasticsearch
多任务库:
- 多线程(threading)、线程队列(queue)
- 协程(asynio、gevent/evevtlet)
爬虫框架:
- scrapy
- scrapy-redis 分布式(多机爬虫)
四、常见反爬虫策略
UA ( User-Agent ) 策略
登录限制(Cookie)策略
请求频次(IP代理)策略
验证码( 图片-云打码、文字或物品图片选择、滑块)
动态json(selenuim/Splash/Api接口)策略
五、爬虫库urllib [重点]
5.1 urllib .resquest模块
5.1.1 简单的请求
from urllib.request import urlopen
#发起网络请求
resp = urlopen('https://www.hao123.com')
assert response.code == 200
print('请求成功')
#保存请求的网页
# f 变量接收open()函数返回的对象的_enter_()返回结果
with open('a.html','wb') as f:
f.write(resp.read())
urlopen(url,data=None)可以直接发起url的请求,如果data不为空时,
则默认是POST请求,反之为GET请求。
resp是https.client.HTTPResponse类对象。
5.1.2 带请求头的请求
def search_baidu_(wd='阿'):
url = 'https://www.baidu.com/s?wd=%s'
#生成请求对象,封装请求url和头header
request =Request(url % quote(wd),
headers={
'Cookie':'BIDUPSID=A6FA40249A7FDACA204C353784FBF5FD; PSTM=1647777387; BAIDUID=A6FA40249A7FDACA4031F9E78BFED697:FG=1; ' 'BAIDUID_BFESS=F0371DF47B0B464C295F844A241C697B:FG=1; BDORZ=B490B5EBF6F3CD402E515D22BCDA1598; BA_HECTOR=agah24al0k850h84fq1h4lojf0r;'
' H_PS_PSSID=36069_36174_31254_36020_36004_36167_34584_36140_36120_36193_36073_35802_26350_35870_36092_36061',
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, '
'like Gecko) Chrome/100.0.4896.60 Safari/537.36'
})
response = urlopen(request) #发起请求
assert response.code == 200
print('请求成功')
#读取响应数据
bytes_ = response.read()
#将响应的数据写入文件中
with open('%s.html' %wd,'wb') as file:
file.write(bytes_)
5.2 urllib.parse模块
- quote()仅对中文字=字符串进行编码;
- urlencode()可以针对一个字典中所有的values进行编码,然后转成key=value&key=value的字符串。
#spider01_urllib.py (爬取成功)
'''
初次使用urllib实现爬虫的数据请求
urllib.request.urlopen(url) 发起get请求
urllib.request.quote() 将中文进行url编码
urllib.request.urlretrieve(url,filename) 下载url保存filename
'''
from urllib.request import urlopen, urlretrieve, Request
from urllib.parse import quote
import ssl
ssl._create_default_https_context = ssl._create_unverified_context
def search_baidu_(wd='阿'):
url = 'https://www.baidu.com/s?wd=%s'
#生成请求对象,封装请求url和头header
request =Request(url % quote(wd),
headers={
'Cookie':'BIDUPSID=A6FA40249A7FDACA204C353784FBF5FD; PSTM=1647777387; BAIDUID=A6FA40249A7FDACA4031F9E78BFED697:FG=1; ' 'BAIDUID_BFESS=F0371DF47B0B464C295F844A241C697B:FG=1; BDORZ=B490B5EBF6F3CD402E515D22BCDA1598; BA_HECTOR=agah24al0k850h84fq1h4lojf0r;'
' H_PS_PSSID=36069_36174_31254_36020_36004_36167_34584_36140_36120_36193_36073_35802_26350_35870_36092_36061',
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, '
'like Gecko) Chrome/100.0.4896.60 Safari/537.36'
})
response = urlopen(request) #发起请求
assert response.code == 200
print('请求成功')
#读取响应数据
bytes_ = response.read()
#?? 当对象进入上下文时,调用对象的哪个方法
#?? 当对象退出上下文时,调用对象的哪个方法
with open('%s.html' %wd,'wb') as file:
file.write(bytes_)
# def download_image(url):
# # 从url中获取文件名
# filename = url[url.rfind('/')+1:]
# req = Request(url,headers={
#
# 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, '
# 'like Gecko) Chrome/100.0.4896.60 Safari/537.36'
#
# })
#
# urlretrieve(url,filename)
# resp = urlopen(req)
# with open (filename,'wb') as file:
# file.write(resp.read())
#
# print(f'下载{filename} ok!')
def download_image(url):
#从url中获取文件名
filename = url[url.rfind('/')+1:]
req = Request(url,headers={
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, '
'like Gecko) Chrome/100.0.4896.60 Safari/537.36'
})
urlretrieve(url,filename) #只能下载简单html文件
if __name__ == '__main__':
# search_baidu_()
# download_image('https://m.dytt8.net/html/gndy/jddy/20220327/62454.html')
# download_image('https://img9.doubanio.com/view/photo/l_ratio_poster/public/p2708481820.jpg')
#spider02_urllib_parse.py (数据包参数和结构和视频里的不同,不知道如何爬取参数,代码直接按照视频里的敲的)
"""
应用 :百度翻译
- urllib.request.Request
- urllib.resquest.urlopen()
- urllib.parse.urlencode()
- 发起 post 请求
"""
import json
from urllib.request import Request, urlopen
from urllib.parse import urlencode
url = 'https://fanyi.baidu.com/sug' # 请求API接口
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:99.0) Gecko/20100101 Firefox/99.0",
"Cookie": "BAIDUID=67CDE4542962E31A5515AF1A7D37CC19:FG=1; BIDUPSID=36B360392903B350B056DC11FBD595D5; PSTM=1648979131; BA_HECTOR=2h200g01248l0h2h991h4qjvk0r; BDORZ=FFFB88E999055A3F8A630C64834BD6D0; BDRCVFR[gltLrB7qNCt]=mk3SLVN4HKm; delPer=0; PSINO=7; "
"H_PS_PSSID=36068_36175_31254_36020_36005_35913_36167_34584_36144_36120_35978_36125_36234_26350_36061; Hm_lvt_64ecd82404c51e03dc91cb9e8c025574=1649233910; ""Hm_lpvt_64ecd82404c51e03dc91cb9e8c025574=1649234228; REALTIME_TRANS_SWITCH=1; "
"FANYI_WORD_SWITCH=1; HISTORY_SWITCH=1; SOUND_SPD_SWITCH=1; SOUND_PREFER_SWITCH=1; APPGUIDE_10_0_2=1; "
"ab_sr=1.0.1_M2U0NWY1NjE2MTFhZmM1MzdlYWZiNWM5N2Y2YmEzMGNhMjE5YjExOTE0N2YxYzM0MTM5YzY0NDNhMzcyYjFmMjlhOTU0N2NlZDY3YWExNzA4M2RhZTBmYTQxMGI4ZTcwY2IzYWY1NTc2MjAzZDJkNWVmOTRkMTg3NzFiYzAzNDFjMTYyMWM5NmU2NjJiZDNhN2U4NmUwZTYzNmNiNDRmYQ=="
,
"X-Requested-With": "XMLHttpRequest"
}
def fanyi(kw):
data = {
'kw': kw
}
# Request() 中的data参数是byte类型
# data 不为空时,是post 请求
req = Request(url, data=urlencode(data).encode('utf-8'))
resp = urlopen(req)
assert resp.code == 200
json_data = resp.read() # byte
content_encode = resp.getheader('Content-Type')
content_encode = 'utf-8' if content_encode is None else content_encode.split('='[-1])
return json.loads(json_data.decode(content_encode))
if __name__ == '__main__':
print(fanyi('orange'))
#spider03_urllib_pages (爬取成功)
"""
复杂GET请求,多页面请求下载
"""
import time
from urllib.request import Request,urlopen
from urllib.parse import urlencode
import ssl
ssl._create_default_https_context = ssl._create_unverified_context
url ='https://www.baidu.com/s?'
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:99.0) Gecko/20100101 Firefox/99.0",
"Cookie": "BAIDUID=67CDE4542962E31A5515AF1A7D37CC19:FG=1; BIDUPSID=36B360392903B350B056DC11FBD595D5; PSTM=1648979131; BA_HECTOR=2h200g01248l0h2h991h4qjvk0r; BDORZ=FFFB88E999055A3F8A630C64834BD6D0; BDRCVFR[gltLrB7qNCt]=mk3SLVN4HKm; delPer=0; PSINO=7; ""H_PS_PSSID=36068_36175_31254_36020_36005_35913_36167_34584_36144_36120_35978_36125_36234_26350_36061; Hm_lvt_64ecd82404c51e03dc91cb9e8c025574=1649233910; ""Hm_lpvt_64ecd82404c51e03dc91cb9e8c025574=1649234228; REALTIME_TRANS_SWITCH=1; ""FANYI_WORD_SWITCH=1; HISTORY_SWITCH=1; SOUND_SPD_SWITCH=1; SOUND_PREFER_SWITCH=1; APPGUIDE_10_0_2=1; ""ab_sr=1.0.1_M2U0NWY1NjE2MTFhZmM1MzdlYWZiNWM5N2Y2YmEzMGNhMjE5YjExOTE0N2YxYzM0MTM5YzY0NDNhMzcyYjFmMjlhOTU0N2NlZDY3YWExNzA4M2RhZTBmYTQxMGI4ZTcwY2IzYWY1NTc2MjAzZDJkNWVmOTRkMTg3NzFiYzAzNDFjMTYyMWM5NmU2NjJiZDNhN2U4NmUwZTYzNmNiNDRmYQ==",
"X-Requested-With": "XMLHttpRequest"
}
params = {
'wd': '' ,
'pn': 0 # 0,10,20,30,... =(n-1)*10
}
def pages_get(wd):
params['wd']=wd
for page in range(1,11):
params['pn'] = (page-1)*10
page_url = url+urlencode(params)
resp = urlopen(Request(page_url,
headers=headers))
assert resp.code == 200
file_name = 'baidu_pages/%s-%s.html' %(wd,page)
with open(file_name,'wb') as f :
bytes_ = resp.read()
f.write(bytes_)
print(f'{file_name} 写入成功!')
time.sleep(0.5)
print('下载%s 10页成功!' % wd)
if __name__ == '__main__':
pages_get('Python3.9')
5.3 HTTP处理器
urllib的请求处理器,主要用于urllib.request.build_opener()
函数参数,表示构造一个由不同处理组成的伪浏览器。
5.3.1 HTTPHandler
处理Http协议的请求处理。
5.3.2 HTTPCookieProcessor
处理Cookie的处理器,创建类实例时,需要提供http.cookiejar.CookieJar
类的实例对象。
5.3.3 ProxyHandler
代理设置
其他
Python上下文的两个核心函数
__enter__(self)
__exit__(self, except_type, except_value, except_tb)
正则中的(), [] , {} 三个符号的作用
( ) 用于分组的符号
[ ] 指定匹配字符的范围,如 [a-c_B-F]
{ } 指定匹配的长度(量词表示)
pymysql.Connect()连接数据库的核心参数
Connect(host,
port=3306,
user,
password,
db,
charset)
一、Python上下文
1.1什么是上下文
任意的Python对象都可以使用上下文环境,使用with关键字。上下文是任意代码片段的区域,当对象进入上下文环境时,解释器会调用对象的__enter__()
,当对象退出上下文环境时,会调用对象的__exit__()
。
1.2为什么使用
对象在使用上下文环境时,为了确保对象正确的释放资源,避免出现**内存遗漏**。
存在以下对象经常使用上下文with:
- 文件操作对象open()
- 数据库的连接conn
- 线程锁Lock
1.3如何使用
1.3.1重写类的方法
上下文的两个核心方法
class A:
def go():
print('up')
def __enter__(self):
#进入上下文时,被调用
#必须 返回一个相关的对象
print('--enter--')
return 'disen'
def __exit__(self,except_type,val,tb):
#退出上下文时,被调用
#except_type 如果存在异常,表示为异常类的实例对象
#val 异常的消息Message
#tb 异常的跟踪栈
#返回True 表示存在异常,不向外抛出
#返回False 表示存在异常,向外抛出
print("--exit--")
return True
1.3.2 with中使用
a =A()
with a as ret:
print(ret)#ret是__enter__的返回值
assert isinstance (ret,str)
print('ok')
--enter--
disen
ok
--exit--
二、dao设计
DAO(Data Access Object)数据访问对象只是一种设计思想,目的是简化数据库成操作。针对实体类(数据,模型类)对象,封装一套与数据库操作的SDK(Software Develop Kit)。
合理的DAO的SDK 设计:
-dao(基础数据库操作模块)
|-- __init__py
|-- base.py
-entity(实体类模块)
|-- user
|-- order
-db (具体数据操作)
|-- user_db.py
|-- order_db.py