admin 管理员组文章数量: 887016
最新模板请见:
Python爬虫模板(v3.0版本)与使用示例_和谐号hexh的博客-CSDN博客
1.模板架构
模板有四个py文件,我放在crawlerTemplate包下。
(1)getAgent模块
# -*- coding: utf-8 -*-
# @Time: 2023-08-20 20:14
# @Author: hexh
# @File: getAgent.py
# @Software: PyCharm
from random import randint
# 随机获取身份
def main():
USER_AGENTS = [
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; AcooBrowser; .NET CLR 1.1.4322; .NET CLR 2.0.50727)",
"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Acoo Browser; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.0.04506)",
"Mozilla/4.0 (compatible; MSIE 7.0; AOL 9.5; AOLBuild 4337.35; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)",
"Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)",
"Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 2.0.50727; Media Center PC 6.0)",
"Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 1.0.3705; .NET CLR 1.1.4322)",
"Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 5.2; .NET CLR 1.1.4322; .NET CLR 2.0.50727; InfoPath.2; .NET CLR 3.0.04506.30)",
"Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN) AppleWebKit/523.15 (KHTML, like Gecko, Safari/419.3) Arora/0.3 (Change: 287 c9dfb30)",
"Mozilla/5.0 (X11; U; Linux; en-US) AppleWebKit/527+ (KHTML, like Gecko, Safari/419.3) Arora/0.6",
"Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.2pre) Gecko/20070215 K-Ninja/2.1.1",
"Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9) Gecko/20080705 Firefox/3.0 Kapiko/3.0",
"Mozilla/5.0 (X11; Linux i686; U;) Gecko/20070322 Kazehakase/0.4.5",
"Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.8) Gecko Fedora/1.9.0.8-1.fc10 Kazehakase/0.5.6",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/535.20 (KHTML, like Gecko) Chrome/19.0.1036.7 Safari/535.20",
"Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; fr) Presto/2.9.168 Version/11.52",
]
return USER_AGENTS[randint(0, len(USER_AGENTS) - 1)]
user-Agent是我们每次发送请求的身份标识。
每次使用不同的user-Agent,可以更好的隐藏身份,防止被封。
参考网址:
爬虫请求网站时报错http.client.RemoteDisconnected: Remote end closed connection without response 请求网站时报错_如果我变成回忆l的博客-CSDN博客
(2)getHTMLByUrllib模块
# -*- coding: utf-8 -*-
# @File: getHTMLByUrllib.py
# @Author: 和谐号
# @Software: PyCharm
# @CreationTime: 2023-08-23 3:25
# @OverviewDescription:
import gzip
import urllib
from io import BytesIO
def main(info,configLog, timeoutTime):
# 根据从目标网页的txt文件中提取请求信息info,正式爬取HTML响应内容
#
# 传入:
# 从目标网页的txt文件中提取请求信息:info ([url,method,data,header])
# 相关配置信息:configLog
# 最大允许等待时间:timeoutTime
#
# 返回一个参数:
# 响应内容:html
# 打包请求信息
if info[1] == "POST":
if configLog["表单数据形式"] == "字典":
data = bytes(urllib.parse.urlencode(info[2]), encoding="utf-8") # 打包data表单
else:
data = info[2].encode("utf-8")
req = urllib.request.Request(url=info[0], headers=info[3], data=data, method="POST")
elif info[1] == "GET":
req = urllib.request.Request(url=info[0], headers=info[3])
else:
print("请求类型错误:", info[1])
return None
try:
# 发送请求,得到响应response
if timeoutTime > 0:
response = urllib.request.urlopen(req, timeout=timeoutTime)
else:
response = urllib.request.urlopen(req)
# 解码responses到html,若是Gzip压缩,二进制文件以"1f8b08"开头,否则直接解码
html = response.read()
if html.hex().startswith("1f8b08"):
buff = BytesIO(html)
f = gzip.GzipFile(fileobj=buff)
html = f.read().decode('utf-8')
else:
html = html.decode('utf-8')
return html
except (urllib.error.URLError, Exception) as e:
if hasattr(e, "code"):
print("urllib报错,响应状态码:", e.code)
if hasattr(e, "reason"):
print("urllib报错,原因:", e.reason)
(3)getHTMLByRequests模块
# -*- coding: utf-8 -*-
# @File: getHTMLByRequests.py
# @Author: 和谐号
# @Software: PyCharm
# @CreationTime: 2023-08-23 3:26
# @OverviewDescription:
import requests
from requests.exceptions import ReadTimeout, HTTPError, RequestException
def main(info, configLog, timeoutTime):
# 根据从目标网页的txt文件中提取请求信息,正式爬取HTML响应内容
#
# 传入:
# 从目标网页的txt文件中提取请求信息:info ([url,method,data,header])
# 相关配置信息:configLog
# 最大允许等待时间:timeoutTime
#
# 返回一个参数:
# 响应内容:html
try:
if info[1] == "POST":
if timeoutTime > 0:
if configLog["ContentType"] in ["json(自动配置)", "json(手动配置)"]:
response = requests.post(info[0], headers=info[3], json=info[2], timeout=timeoutTime)
else:
response = requests.post(info[0], headers=info[3], data=info[2], timeout=timeoutTime)
else:
if configLog["ContentType"] in ["json(自动配置)", "json(手动配置)"]:
response = requests.post(info[0], headers=info[3], json=info[2])
else:
response = requests.post(info[0], headers=info[3], data=info[2])
elif info[1] == "GET":
if len(info[2]) > 0: # 这里或许能优化
if timeoutTime > 0:
response = requests.get(info[0], headers=info[3], params=info[2], timeout=timeoutTime)
else:
response = requests.get(info[0], headers=info[3], params=info[2])
else:
if timeoutTime > 0:
response = requests.get(info[0], headers=info[3], timeout=timeoutTime)
else:
response = requests.get(info[0], headers=info[3])
else:
print("请求类型错误:", info[1])
return None
# 解码:
if response.status_code == 200:
html = response.text # 如果输出乱码,这里可以考虑使用contents属性
return html
else:
print('请求失败,状态码:', response.status_code)
print('Error response:', response.text)
return None
except ReadTimeout as e:
print('Timeout', e)
except HTTPError as e:
print('Http error', e)
except RequestException as e:
print('Error', e)
(4)crawler模块
# -*- coding: utf-8 -*-
# @Time: 2023-08-20 22:23
# @Author: hexh
# @File: crawler.py
# @Software: PyCharm
from crawlerTemplate import getAgent, getHTMLByUrllib, getHTMLByRequests
def toDict(theList, noNeedKey):
# 将data或header的格式,从字符串list转换为字典
#
# 传入:
# 待转换的list:List
# 不需要的字段key列表:noNeedKey
#
# 返回一个参数:
# 转换后的字典:res
res = {}
for item in theList:
if ":" not in item:
continue
i = item.index(":")
if item[0:i] in noNeedKey:
continue
res[item[0:i]] = item[i + 2:-1] if item.endswith("\n") else item[i + 2:]
return res
def getRequestInfoFromTxt(path, data, headerNoneedKey):
# 从目标网页的txt文件中提取请求信息
#
# 传入:
# txt文件路径:path
# 手动配置的数据表单:data:
# header中不需要的key:headerNoneedKey
#
# 返回一个参数:列表info
# [url, method, data, header]
# 读
contextList = []
try:
f = open(path, "r", encoding='utf-8')
try:
contextList = f.readlines()
except Exception as e:
print(e)
finally:
f.close()
except Exception as e:
print(e)
# 解析:
header = []
url = ""
method = "未检测出请求类型,请检查配置文件"
tmp = "请求 URL:\n"
try:
if tmp in contextList:
i = contextList.index(tmp)
if data == "auto":
data = contextList[0:i]
url = contextList[i + 1][0:-1]
method = contextList[i + 3][0:-1]
header = contextList[i + 10:]
header = toDict(header, headerNoneedKey)
except Exception as e:
print("txt文件配置错误", e)
return [url, method, data, header]
def config(data, info, libraryUsed, isPrint):
# 记录、更改爬虫配置
#
# 传入:
# 主方法中手动配置的数据表单:data //主要判断是否是auto
# txt文件中读取的信息:info
# 主方法中选择的爬虫库:libraryUsed
# 是否打印配置信息:isPrint
#
# 返回一个参数:
# 配置日志:configLog
configLog = {"表单数据获取方式": None, "表单数据形式": None, "爬虫库": None, "ContentType": None}
# 判断表单数据获取方式,并自动获取表单数据形式
if data == "auto":
configLog["表单数据获取方式"] = "自动获取"
configLog["表单数据形式"] = "字符串" if len(info[2]) == 1 else "字典"
else:
configLog["表单数据获取方式"] = "手动配置"
if isinstance(info[2], dict):
configLog["表单数据形式"] = "字典"
elif isinstance(info[2], str):
configLog["表单数据形式"] = "字符串"
else:
configLog["表单数据形式"] = "错误"
# 随机User-Agent
if info[3].get("User-Agent") == "True":
info[3]["User-Agent"] = getAgent.main()
print("本次采用的随机User-Agent为:", info[3]["User-Agent"])
# 读取header中的Content-Type,用于判断是否用json=data
tmpList = ["content-type", "Content-type", "content-Type", "Content-Type"]
contentType = ""
for item in tmpList:
contentType = info[3].get(item, "")
if contentType != "":
break
configLog["ContentType"] = "json(手动配置)" if "json" in contentType else "非json(手动配置)"
# 如果libraryUsed没有传入,即为默认值auto,则自动优化配置“爬虫库”和“ContentType”
if libraryUsed == "auto":
if configLog["表单数据形式"] == "字典":
configLog["爬虫库"] = "requests(自动配置)"
configLog["ContentType"] = "json(自动配置)"
elif configLog["表单数据形式"] == "字符串":
configLog["爬虫库"] = "requests(自动配置)"
configLog["ContentType"] = "非json(自动配置)"
elif libraryUsed == "r":
configLog["爬虫库"] = "requests(手动配置)"
elif libraryUsed == "u":
configLog["爬虫库"] = "urllib(手动配置)"
else:
configLog["爬虫库"] = "错误"
if isPrint:
for key, value in configLog.items():
print(key + ":" + value)
return configLog
def dataProcessing(info, configLog):
# 表单数据data的处理
#
# 传入:
# txt文件中读取的信息:info
# 配置日志:configLog
#
# 无返回值
if configLog["表单数据获取方式"] == "自动获取":
if configLog["表单数据形式"] == "字典":
info[2] = toDict(info[2], [])
elif configLog["表单数据形式"] == "字符串":
info[2] = info[2][0][:-1]
def main(filepath, libraryUsed="auto", data="auto", isPrint=True,timeoutTime=0, headerNoneedKey=None):
# 爬虫主函数,根据提供的目标网站txt(文件路径),返回爬虫结果
# txt文件要求:
# 先从F12中显示原始,将浏览器请求信息,拷贝到txt文件
# 如果是POST方法,需要将表单data拷贝到请求信息前
# 如果需要采用随机user-Agent,请将txt中该行设置为"User-Agent: True",注意True前有空格,后无空格
#
# 传入:
# 目标网页的txt配置文件路径:filepath //必填参数
# 爬虫库选择参数:libraryUsed //如果不写,默认为"auto",自动配置爬虫库和ContextType。可选参数:"r":requests库,"u":urllib库
# 表单数据:data //如果不写,默认为"auto",自动从txt中获取,否则用形参中的data
# 是否打印配置信息:isPrint //如果不写,默认为True,打印配置信息
# 爬虫timeout秒数,即最多等服务器反应的时间:timeoutTime //如果不写,默认为0,即不设置
# header中不需要的键:headerNoneedKey //一般不写,取默认值["Date", "Server", "Transfer-Encoding"]
#
# 返回一个参数:
# 响应内容:html
#
# requests库有时比urllib更快,但在使用requests库时要注意表单类型(json类型/data类型)
# 一般来说,header里如果content-type里包含了json字样,就是json类型,json=data;否则是data类型,data=data
# 如果出现错误:400,Error response: {"message":"Expecting object or array (near 1:1)","status":400}
# 很有可能是context-type配置错了,可以取消libraryUsed自动配置,在txt中手动配置context—type
# 另外注:urllib目前不分json和data,统一是data,只有用requests库时要考虑
if headerNoneedKey is None:
headerNoneedKey = ["Date", "Server", "Transfer-Encoding"]
info = getRequestInfoFromTxt(filepath, data, headerNoneedKey)
configLog = config(data, info, libraryUsed,isPrint)
# 配置检查:
if None in configLog.values() or "错误" in configLog.values():
print("配置错误")
return None
dataProcessing(info, configLog)
if configLog["爬虫库"] in ["urllib(自动配置)", "urllib(手动配置)"]:
return getHTMLByUrllib.main(info, configLog, timeoutTime)
elif configLog["爬虫库"] in ["requests(自动配置)", "requests(手动配置)"]:
return getHTMLByRequests.main(info, configLog, timeoutTime)
2.代码中的一些解释
(如果你只是想用模板的话,这部分可以跳过不看)
(1)导包
自定义包和模块要放在软件包里,不要放在目录里,否则会带来麻烦。
如果使用目录,当两个文件不在一起时,可能会因搜索不到而报错。此时可以用sys.path方法添加系统路径来解决。但这样在导包的地方仍然会报错,程序能够运行。
而如果用软件包的话,两个文件或许可以离得更远,不用sys.path就行,也不会报错。
(2)header中的参数
因为并不是所有的header都是有用的,也不是header越多越好
像我例子中的url,只要cookie中的JSESSIONID和array就可以了,其它都没有都可以。
但是如果有,必须要写对,错一个都可能不行。
特别的,Transfer-Encoding: chunked不能加到header里。
所以说,有些header是没用的,将其写到headerNoneedKey里,并设置了默认值。
(3)关于cookie
JSESSIONID,就像是一把密钥,当我们在登录系统完成,服务器会给我们一个JSESSIONID,然后在系统内,我们每次请求都要带着这把密钥,才能得到响应,这个密钥里也包含了我们的一些身份信息。当然,这个密钥也是会过期的,与浏览器的打开与关闭无关。每次登录都会得到一个新JSESSIONID,但是原来的旧JSESSIONID只要没过期,仍然可以用。
(4)gzip压缩
有些网页是gzip压缩的,与一般的读取方式不同,识别方法就是先hex一下,看是不是以"1f8b08"开头。
3.参数介绍
需要的确定的参数:
(1)txt文件中
url(目标网址),method(POST或GET),data(表单数据),header(请求头)
其中,header中有三个比较重要的字段:
①cookie:与登录有关
②User-Agent:当前身份。如果需要随机身份,需要将其值设置为:“ True”
③Context-Type:表单数据的文本类型,如果出现json字样,则要用json类型,没有则为data类型。更准确的方法是根据表单数据取判断,如果有列表[],特殊情况,建议用json类型。
(2)crawler.main()方法形参
最重要的就是txt文件的路径filepath,必填。
其余的都可以使用默认值,程序会自动配置。
如果报错,可以试着调调libraryUsed和Context-Type,以及data
def main(filepath, libraryUsed="auto", data="auto", isPrint=True,timeoutTime=0, headerNoneedKey=None):
# 爬虫主函数,根据提供的目标网站txt(文件路径),返回爬虫结果
# txt文件要求:
# 先从F12中显示原始,将浏览器请求信息,拷贝到txt文件
# 如果是POST方法,需要将表单data拷贝到请求信息前
# 如果需要采用随机user-Agent,请将txt中该行设置为"User-Agent: True",注意True前有空格,后无空格
#
# 传入:
# 目标网页的txt配置文件路径:filepath //必填参数
# 爬虫库选择参数:libraryUsed //如果不写,默认为"auto",自动配置爬虫库和ContextType。可选参数:"r":requests库,"u":urllib库
# 表单数据:data //如果不写,默认为"auto",自动从txt中获取,否则用形参中的data
# 是否打印配置信息:isPrint //如果不写,默认为True,打印配置信息
# 爬虫timeout秒数,即最多等服务器反应的时间:timeoutTime //如果不写,默认为0,即不设置
# header中不需要的键:headerNoneedKey //一般不写,取默认值["Date", "Server", "Transfer-Encoding"]
#
# 返回一个参数:
# 响应内容:html
#
# requests库有时比urllib更快,但在使用requests库时要注意表单类型(json类型/data类型)
# 一般来说,header里如果content-type里包含了json字样,就是json类型,json=data;否则是data类型,data=data
# 如果出现错误:400,Error response: {"message":"Expecting object or array (near 1:1)","status":400}
# 很有可能是context-type配置错了,可以取消libraryUsed自动配置,在txt中手动配置context—type
# 另外注:urllib目前不分json和data,统一是data,只有用requests库时要考虑
4.模板使用
(1)准备目标网站的txt文件:
url,method,data,header 这些可以从F12中找到(注意把“原始”勾上)
url对应请求url,method对应请求方法,header对应响应标头+请求标头
如果是POST方法,data在负载里可以看到:
我们将这些内容放到一个txt文件中(data在前,其它直接复制在后面):
例如:
ordered: true
sortType: desc
请求 URL:
https://jwgl.dhu.edu/dhu/common/semesterSS
请求方法:
POST
状态代码:
200 OK
远程地址:
218.193.151.149:443
引用者策略:
strict-origin-when-cross-origin
HTTP/1.1 200 OK
x-frame-options: SAMEORIGIN
Pragma: no-cache
Cache-Control: no-cache, no-store, max-age=0
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Content-Type: application/json;charset=UTF-8
Content-Language: zh-CN
Date: Sun, 20 Aug 2023 21:43:15 GMT
Server:
Set-Cookie: array=jwgl_01; Secure
Transfer-Encoding: chunked
Connection: Keep-alive
Via: 1.1 ID-0016035530113266 uproxy-3
POST /dhu/common/semesterSS HTTP/1.1
Accept: application/json, text/javascript, */*; q=0.01
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Connection: keep-alive
Content-Length: 26
Content-Type: application/x-www-form-urlencoded;charset=UTF-8
Cookie: array=jwgl_01; array=jwgl_01; JSESSIONID=; array=jwgl_01; iPlanetDirectoryPro=
Host: jwgl.dhu.edu
Origin: https://jwgl.dhu.edu
Referer:
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
User-Agent: Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Mobile Safari/537.36 Edg/115.0.1901.203
X-Requested-With: XMLHttpRequest
sec-ch-ua: "Not/A)Brand";v="99", "Microsoft Edge";v="115", "Chromium";v="115"
sec-ch-ua-mobile: ?1
sec-ch-ua-platform: "Android"
这样我们称为一个url的配置文件txt。
将要爬取的网页的txt文件都按照上面所讲的方法准备好,放到target目录下:
(2)方法调用
在目录新建一个文件main.py,调用我们的模板:
# -*- coding: utf-8 -*-
# @Time: 2023-08-18 16:33
# @Author: hexh
# @File: main.py
# @Software: PyCharm
import os
import random
from crawlerTemplate import crawler
import time
if __name__ == "__main__":
start = time.time()
folderpath = r"./target"
os.chdir(folderpath)
for i, item in enumerate(os.listdir()):
print(item[:-4] + ":")
print(str(i) + "\t爬取结果为:\n", crawler.main(item))
print()
# time.sleep(random.randint(-2500,2500)/1000+5)
end = time.time()
print(end - start)
# encoded_data = json.dumps(json_data).encode("utf-8")
① 其中,end-start是我调试时用来计时的,一般可以不写。
② 中间可以设置一个几秒的休眠,避免被封。
得到数据后就可以去做数据解析和可视化了。
版权声明:本文标题:Python爬虫数据获取模板与使用方法(v2.0版本) 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.freenas.com.cn/jishu/1726377477h948217.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论