常州建设企业网站,帮人做图挣外快的网站,网站建设低价建站,wordpress 新手免责声明 本文的爬虫知识仅用于合法和合理的数据收集#xff0c;使用者需遵守相关法律法规及目标网站的爬取规则#xff0c;尊重数据隐私#xff0c;合理设置访问频率#xff0c;不得用于非法目的或侵犯他人权益。因使用网络爬虫产生的任何法律纠纷或损失#xff0c;由使用…免责声明 本文的爬虫知识仅用于合法和合理的数据收集使用者需遵守相关法律法规及目标网站的爬取规则尊重数据隐私合理设置访问频率不得用于非法目的或侵犯他人权益。因使用网络爬虫产生的任何法律纠纷或损失由使用者自行承担风险和责任。 1、认识爬虫
1.1、初识爬虫
概述网络爬虫也叫网络蜘蛛如果把互联网比喻成一个蜘蛛网那么蜘蛛就是在网上爬来爬去 的蜘蛛爬虫程序通过请求url地址根据响应的内容进行解析采集数据。简而言之就是用代码模拟人的行为去各各网站溜达、点点按钮、查查数据。或者把看到的数据拿下来。
爬虫的作用通过有效的爬虫手段批量采集数据可以降低人工成本提高有效数据量给予运营/销售的数据支撑加快产品发展。 1.2、爬虫的合法性
概述爬虫有时合法有时不合法因情况而定利用程序进行批量爬取网上的公开信息(前端显示的数据信息)因为信息是完全公开的所以是合法的但是通过爬虫获取隐私信息或对目标网站造成DDOS攻击就是不合法的爬虫。
合法的爬虫①、公开且没有标识不可爬取的数据②、不影响目标网站的服务器③、不影响目标网站的业务。
非法爬虫①、获取用户数据②、明文规定不能爬取的数据(1、在域名后加上/robots.txt查看网站标明的爬取规则但不是所有网站都有。2、页面上标明不允许爬取)③、影响目标网站业务(占用网站大量资源导致正常用户无法访问)④、影响目标网站服务器(由于频率过高对服务器造成DDOS攻击)
实操1查看bilibili的爬虫规则。
在浏览器的地址栏中输入如下地址
www.bilibili.com/robots.txt
运行结果如下 结果分析
# /index.htmlUser-agent: *表示这是对所有爬虫的规则禁止所有爬虫访问 /medialist/detail/ 路径禁止所有爬虫访问 /index.html 页面。User-agent: * Disallow: /medialist/detail/ Disallow# 下面是针对特定爬虫的规则,表示允许这些特定的爬虫例如 Yisouspider、Applebot、bingbot 等访问网站的所有部分。
User-agent: Yisouspider
Allow: /User-agent: Applebot
Allow: /User-agent: bingbot
Allow: /User-agent: Sogou inst spider
Allow: /User-agent: Sogou web spider
Allow: /User-agent: 360Spider
Allow: /User-agent: Googlebot
Allow: /User-agent: Baiduspider
Allow: /User-agent: Bytespider
Allow: /User-agent: PetalBot
Allow: /
# 最后又给了一个针对所有爬虫的规则,该条规则禁止所有爬虫访问网站的所有部分。
User-agent: *
Disallow: /
注意该文件存在矛盾之处因为它对所有爬虫User-agent: *先是指定了允许和禁止的部分但在文件最后又禁止了所有访问但是根据标准最后一条匹配的规则会覆盖之前的规则。因此所有的爬虫最终都会被禁止访问整个网站尽管之前有针对特定爬虫的允许规则。 1.3、反爬与反反爬
反爬企业不想让自己的数据被别人拿到这时就会设置反爬的手段不让爬虫获取数据。
常用反爬手段①、合法检测请求校验(useragentreferer接口加签等)②、验证码识别文字、做题、滑动等③、小黑屋IP/用户限制请求频率或者直接拦截④、投毒反爬虫高境界可以不用拦截拦截是一时的投毒返回虚假数据可以误导竞品决策。
反反爬破解掉反爬手段再获取其数据。 1.4、爬虫的基本流程
基本流程
①、确定想要获取哪些数据
②、获取目标url获取目标数据来源于哪个网站并通过地址栏将这个网站的url获取
③、分析结构分析目标数据具体放在哪个位置该数据是如何展现出来的
④、构思爬取策略
⑤、编码 基本手段
①、破解请求限制1、请求头设置如useragant为有效客户端2、控制请求频率(根据实际情景)3、IP代理4、签名/加密参数从html/cookie/js分析。
②、破解登录授权请求中带上用户cookie信息。
③、破解验证码简单的验证码可以使用识图读验证码第三方库。 解析获取到的数据
①、HTML Dom解析1、正则匹配通过的正则表达式来匹配想要爬取的数据如有些数据不是在html 标签里而是在html的script 标签的js变量中2、使用第三方库解析html dom比较喜欢类jquery的库。
②、数据字符串(json)1、正则匹配(根据情景使用)2、转 JSON/XML 对象进行解析 问很多编程语言都能实现网络爬虫为什么要选择Python
答Python简单高效第三方模块库多。 1.5、浏览器开发工具
概述对于爬虫来说最核心的就是发送请求让网络服务器返回相应的数据。而最为核心之一就是找到URL这时就需要一个可以帮助我们分析URL的工具----浏览器开发者工具(通过F12打开)。 实操2关闭浏览器工具中网络标签下无用的窗口信息。
首先打开谷歌浏览器按F12打开浏览器开发工具然后按如下图所示步骤操作 修改后的浏览器工具网络标签的界面如下 实操3将浏览器工具的语言切换成中文。
首先打开谷歌浏览器按F12打开浏览器开发工具然后按如下图所示步骤操作 关闭浏览器开发工具再次打开后的结果如下 2、urllib开发爬虫
2.1、第一个爬虫
概述urllib模块库是python3自带的在Python2叫urllib2。
实操4获取百度响应信息中前300个字符并打印。
from urllib.request import urlopen
# 请求地址
url http://www.baidu.com# 发送请求
result urlopen(url)# 打印响应结果
# read() 方法主要用于从文件对象中读取数据,decode()方法是将字节数据转换为字符串的主要工具
print(result.read().decode()[:300])
运行结果如下 2.2、urllib的基本用法
语法1
requset.urlopen(url,data,timeout)
语法解析
①、urlopen方法用于发送请求
②、第一个参数url即为URL是必须要传送的。第二个参数data是访问URL时要传送的数据第三个timeout是设置超时时间
③、data和timeout是可选参数data默认为空Nonetimeout默认为socket._GLOBAL_DEFAULT_TIMEOUT。 语法2
response.read()
语法解析read()方法就是读取文件里的全部内容返回bytes类型。 语法3
response.getcode()
语法解析getcode方法用于返回 HTTP的响应码200表示请求成功404表示请求的资源不存在500表示服务器发送错误。 语法4
response.geturl()
语法解析geturl方法用于返回实际数据的实际URL防止重定向问题。 语法5
response.info()
语法解析info方法用于返回服务器响应的HTTP报头。 实操5通过百度测试urllib中所有的用法。
from urllib.request import urlopen
# 请求地址
url http://www.baidu.com# 发送请求
response urlopen(url)# 打印响应结果
# read() 方法主要用于从文件对象中读取数据,decode()方法是将字节数据转换为字符串的主要工具
print(f请求到的内容为:{response.read().decode()[:50]})
print(f请求的状态码:{response.getcode()})
print(f请求的url为:{response.geturl()})
print(fhttp报头信息:{response.info()})
运行结果如下 2.3、request对象的使用
概述使用urllib.request.urlopen发送请求时可以将参数封装到一个Request对象中。
封装的参数①、发送的请求链接url②、请求头信息headers③、请求数据data。
知识点补充http://httpbin.org/get 是一个用于测试 HTTP 请求的端点由 httpbin.org 提供。它是一个简单而实用的服务用于测试各种 HTTP 请求和响应。访问这个 URL 会返回一个包含有关请求的详细信息的 JSON 响应。这对于开发和调试网络应用程序非常有帮助因为你可以查看发送的请求的各个方面以及服务器返回的响应。
实操6通过request获取http://httpbin.org/get的所有返回信息。
from urllib.request import Request,urlopen# 准备链接
url http://httpbin.org/get# 创建request对象
req Request(url)# 发送请求
resp urlopen(req)# 打印响应结果
print(resp.read().decode())
运行结果如下 问题引入通过上面的信息可知通过程序直接请求别人的网站时我们的User-Agent显示的是Python-urllib/3.11这明显是一个程序名而不是一个浏览器。 获取User-Agent的方式
①、通过浏览器访问http://httpbin.org/get获取User-Agent请求后的结果如下 ②、通过浏览器开发工具获取User-Agent 实操7为我们的网络爬虫伪装一个User-Agent并用程序访问http://httpbin.org/get验证是否成功。
from urllib.request import Request,urlopen
# 准备链接
url http://httpbin.org/get# 定义伪装的header信息
header {User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36}# 创建request对象
req Request(url,headersheader)# 发送请求
resp urlopen(req)# 打印响应结果
print(resp.read().decode())
运行结果如下 发送请求/响应header头的含义
名称含义Accept告诉服务器客户端支持的数据类型Accept-Charset告诉服务器客户端采用的编码Accept-Encoding告诉服务器客户机支持的数据压缩格式Accept-Language告诉服务器客户机的语言环境Host客户机通过这个头告诉服务器想访问的主机名If-Modified-Since客户机通过这个头告诉服务器资源的缓存时间Referer客户机通过这个头告诉服务器它是从哪个资源来访问服务器的。一般用于防盗链User-Agent客户机通过这个头告诉服务器客户机的软件环境Cookie客户机通过这个头告诉服务器可以向服务器带数据Refresh服务器通过这个头告诉浏览器隔多长时间刷新一次Content-Type服务器通过这个头回送数据的类型Content-Language服务器通过这个头告诉服务器的语言环境Server服务器通过这个头告诉浏览器服务器的类型Content-Encoding服务器通过这个头告诉浏览器数据采用的压缩格式Content-Length服务器通过这个头告诉浏览器回送数据的长度 2.4、urllib发送get请求
问题引入用浏览器进行搜索时如果内容包含中文浏览器会将我们的中文内容进行转码因此当我们的参数中包含中文时就需要使用特定的方法对中文进行转码下图是网页中转码的实际截图 概述Get请求的参数都是在Url中体现的如果有中文就需要转码这时我们可使用①、urllib.parse.urlencode() 转换键值对②、urllib.parse. quote() 转换一个值。
实操8利用爬虫获取百度中任意带有中文信息的搜索结果页面信息。
from urllib.request import Request,urlopen
from urllib.parse import quote,urlencode
# 准备url
def quote_url():args python学习url fhttps://www.baidu.com/s?wd{quote(args)}return url
def urlencode_url():args {wd:python学习}url fhttps://www.baidu.com/s?{urlencode(args)}return url# 准备header信息
header {User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36}# 创建Request对象(分别传入不同函数内的url验证是否两种方式都有效)
req Request(quote_url(),headersheader)
# req Request(urlencode_url(),headersheader)# 发送请求
resp urlopen(req)# 打印获取的信息
print(resp.read().decode()[:1080])运行结果如下 2.5、实战之某瓣电影TOP250
实操9获取某瓣电影 Top 250中任意连续页面的网页信息要求每页只打印前2000个字符串信息。
# 以下是该网站的前三页地址
https://movie.douban.com/top250?start0filter
https://movie.douban.com/top250?start50filter
https://movie.douban.com/top250?start100filter
通过观察地址不难发现不同的网页信息通过start参数来区分。
import time
from urllib.request import Request,urlopen
from time import sleep
def myCrawler_top250(start,end):# 构造url地址for i in range(start*50-50,end*501,50):url fhttps://movie.douban.com/top250?start{i}filterheader {User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36}# 创建请求对象req Request(url,headersheader)# 发送网络请求resp urlopen(req)# 读取并打印返回信息print(resp.geturl()) # 打印爬取的urlprint(resp.getcode()) # 打印状态码print(resp.read().decode()[:2000]) # 打印返回的内容# 休眠1秒,缓解目标网站的压力time.sleep(1)if __name__ __main__:# 获取第4页到第6页的内容myCrawler_top250(4,6)
运行结果如下 2.6、urllib发送post请求
概述POST请求的参数data需要放到Request请求对象中data是一个字典里面要匹配键值对。
实操10获取百度登录的地址并尝试利用urllib向百度发送一个用于登录的post请求。
①、首先进入百度官网点击登录并获取登录网址 ②、获取data参数需要的键值对信息 ②、通过urllib发送post请求
from urllib.request import Request,urlopen
from urllib.parse import urlencodeurl https://passport.baidu.com/v2/api/?login
header {User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36}
# 封装数据
data {staticpage: https://www.baidu.com/cache/user/html/v3Jump.html,charset: UTF-8,token: 13473c00b25744c8f2a86c321690fec2,tpl: mn,apiver: v3,tt: 1718798412406,safeflg: 0,u: https://www.baidu.com/,detect: 1,gid: 881F86A-C6FA-4661-B854-4EAFC0C9EF9F,quick_user: 0,logintype: dialogLogin,logLoginType: pc_loginDialog,loginmerge: true,splogin: rate,username: 123,password: 123
}
# urlencode将字典转换为URL编码的字符串
encode_data urlencode(data).encode()# 创建一个请求对象
req Request(url,headersheader,dataencode_data)# 发送请求
resp urlopen(req)# 打印返回的信息
print(resp.read().decode())
运行结果如下 由上图可知我们并没有成功发送post请求实现登录大家可以参考程序中的思路去实现urllib发送post请求。 2.7、动态页面的数据获取
静态页面特征访问有UI页面URL可以直接获取数据
动态页面特征访问有UI页面URL不能获取数据需要抓取新的请求获取数据。
概述有些网页内容使用AJAX加载而AJAX一般返回的是JSON,直接对AJAX地址进行post或get就返回JSON数据了
实操11判断虎扑网是动态网页还是静态网页。
方式一通过搜索内容判断如果网页源码内有主页中的任意标题名就说明是静态网页(避免搜索前面的几个标题因为部分网页可能会将前几条数据在前端写死)
①、下滑首页获取任意一个标题名(可以只复制一部分防止内容中间有其他样式标签) ②、检查整页源码 ③、通过CtrlF查询复制的标题搜索不到就说明这是一个动态网站能搜索到就说明是一个静态网页。 方式二通过浏览器开发工具判断如果有xhr文件该网站大概率就是动态网站。 实操12获取动态页面中任意一页的数据。
from urllib.request import Request,urlopen# 定义目标地址headers
url https://www.hupu.com/home/v1/news?pageNo3pageSize50
header {User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36}# 定义Request对象
req Request(url,headersheader)# 发送请求
resp urlopen(req)# 打印获取的信息
# 读取响应内容并解码
context resp.read().decode()
# 将解码后的内容中所有的,替换成换行将展示结果平铺
content_with_line_breaks context.replace(,, \n)
# 打印解码并自动换行的结果
print(f{content_with_line_breaks})
运行结果如下 3、爬虫相关技术
3.1、请求SSL证书验证
概述
①、现在随处可见 https 开头的网站urllib可以为 HTTPS 请求验证SSL证书就像web浏览器一样如果网站的SSL证书是经过CA认证的则能够正常访问如https ://www.baidu.com/百度一下你就知道。
②、如果SSL证书验证不通过或者操作系统不信任服务器的安全证书比如浏览器在访问远古时期的12306网站的时候会警告用户证书不受信任。
SSL问题的参考代码 import ssl# 忽略SSL安全认证
context ssl._create_unverified_context()
# 添加到context参数里
response urlopen(request, context context) 3.2、伪装请求头
概述在前面的学习中我们通过网页中User-Agent的值设置了请求头但这样会产生两个问题①、获取较繁琐②、目标网站认为该用户的请求较频繁(因为一直是一个固定的User-Agent在请求)。基于以上的问题就考虑使用fake-useragent。
安装 # 在终端输入(ALTF12)
pip install fake-useragent 运行结果如下 实操13验证fake-useragent能否产生多个不同的User-Agent信息。 from fake_useragent import UserAgent
# 创建UserAgent对象
ua UserAgent()
# 利用UserAgent对象获取不同浏览器的User-Agent信息(每次运行都有不同的结果)
print(ua.chrome)
print(ua.chrome)
print(ua.firefox)
print(ua.firefox)
print(ua.edge)
print(ua.edge) 运行结果如下 实操14通过fake-useragent生成Chrome的User-Agent并访问http://httpbin.org/get验证结果。 from urllib.request import Request,urlopen
from fake_useragent import UserAgent
# 创建UserAgent对象
ua UserAgent()
url http://httpbin.org/get
# 创建request对象
req Request(url,headers{User-Agent:ua.chrome})# 发送请求
resp urlopen(req)# 打印响应信息
print(resp.read().decode()) 运行结果如下 3.3、设置代理
概述爬虫设置代理就是让别的服务器或电脑代替自己的服务器去获取数据。
爬虫代理源码 代理分类
①、透明代理目标网站知道你使用了代理并且知道你的源IP地址这种代理显然不符合我们这里使用代理的初衷
②、匿名代理匿名程度比较低也就是网站知道你使用了代理但是并不知道你的源IP地址
③、高匿代理这是最保险的方式目标网站既不知道你使用的代理更不知道你的源IP。 注意代理IP无论是免费还是付费都不能保证一定可用。 实操15模拟不使用代理和使用代理。
########## 不使用代理 ##########
from urllib.request import Request,build_opener,ProxyHandler
from fake_useragent import UserAgenturl http://httpbin.org/get
ua UserAgent()
header {User-Agent:ua.chrome}# 创建请求对象
req Request(url,headersheader)# 构建一个可以使用代理的控制器
# ProxyHandler({type:ip:port})
# type指http或https
handler ProxyHandler()
# 创建一个能够处理特定类型请求的 opener比如处理 HTTP 重定向、认证、代理等
opener build_opener(handler)
# 使用自定义的 opener 打开 URL
resp opener.open(req)# 打印响应信息
print(resp.read().decode())
运行结果如下 然后去网上找一个免费的代理并将类型ip地址和端口号填入ProxyHandler方法中以下是我找到的免费代理 修改后的代码如下
from urllib.request import Request,build_opener,ProxyHandler
from fake_useragent import UserAgenturl http://httpbin.org/get
ua UserAgent()
header {User-Agent:ua.chrome}# 创建请求对象
req Request(url,headersheader)# 构建一个可以使用代理的控制器(创建代理处理器)
# ProxyHandler({type:ip:port})
# type指http或https
handler ProxyHandler({https:119.39.76.253:80})
# 创建一个能够处理特定类型请求的 opener比如处理 HTTP 重定向、认证、代理等
opener build_opener(handler)
# 使用自定义的 opener 打开 URL
resp opener.open(req)# 打印响应信息
print(resp.read().decode())
运行结果如下 3.4、cookie的使用
概述Cookie 是存储在用户浏览器中的小数据文件用于保存用户的会话信息、偏好设置和跟踪用户行为以便网站在后续访问时能够提供个性化服务和维持状态。
实操16获取某牙直播平台个人信息中的银豆数量(要登录才能获取到) # 当请求头不包含cookie时
from urllib.request import urlopen,Request
from fake_useragent import UserAgenturl https://i.huya.com/
header {User-Agent:UserAgent().chrome}
# 创建请求对象
req Request(url,headersheader)
# 发送请求
resp urlopen(req)
# 获取响应信息
with open(myblog.html,w,encodingutf-8) as f:f.write(resp.read().decode(utf-8))
获取的文件内容如下 通过搜索并不能找到我们的银豆信息。
然后通过浏览器开发工具--网络--表头--复制cookie信息--将cookie信息补充到请求头
from urllib.request import urlopen,Request
from fake_useragent import UserAgenturl https://i.huya.com/
header {User-Agent:UserAgent().chrome,Cookie:udb_guiddata79a3df881af44e69a33b38efcf5534ec; udb_deviceidw_766286372399177728; __yamid_tt10.2543070640208269; __yamid_newCA79F7AF9500000186581840F3A07DB0; game_didwEnyJNHSc3IPY42Ye1gYhtXpuxBwho7u81J; alphaValue0.80; sdidshorttesttest; first_username_flag_first_1; guid0a7d311d6cc52c6548014efb4ffb8854; huya_uawebh51.0.0huya; SoundValue1.00; videoBitRate4000; Hm_lvt_51700b6c722f5bb4cf39906a596ea41f1714224261,1714567558,1715442596,1715782218; isInLiveRoom; guid0a7d311d6cc52c6548014efb4ffb8854; udb_passdata3; __yasmid0.2543070640208269; udb_biztokenAQAgl54WAlOTGGJfulS_jJzqYrugI97HI6dEqqCl32EkkyW31z1Mg-eC5EwqXLKnNSgWGEMH4azeTCR6eg2f-5hvNFTP_CAdgteny7G7ewQUqQc5dV2cBHQMAr45IVuXlRXTl6eKANOlwOgYdIXwaFyCBZbzengk0Do2r6O9E8gPI5yDLKiHVRRo1RuxPlJ-ARDq-3mxSLGFZ71YzT3CY3yUink5iovbxUgB1T04YM3jUvI6SmRCsdcVBf4lnh-WXIczW7lw0-6fjztHcFCeDdhbpDsd8dWpdt-nliiJocb3I8CvcbczhAF61otmgvcN_SnNNyBn4XKkzPU6HFp2-_BQ; udb_credChDUt7N2ZX7y6sPmWk6hW_Qvoen22xzSYvISKgsb4af2E5x1eery2EMZ_mWY-UJjVXECG2iKF2gonDty-X-Its1FtEF2rLykh-3NDbcTk-uIKZOMBeIBgOlVUTMcnkHmiCJLdAkqkUaxVhOrsj30fNwq; udb_origin100; udb_other%7B%22lt%22%3A%221719045090434%22%2C%22isRem%22%3A%221%22%7D; udb_status10; udb_uid2301608087; udb_version1.0; yyuid2301608087; h_unt1719045090; __yaoldyyuid2301608087; _yasids__rootsid%3DCACA7AABA6100001B5411115C10098E0; sdid0UnHUgv0_qmfD4KAKlwzhqZb5S6OXLz9qXc5mn5jmM9WUEih_2-hvJz15SCjqFv-OTJvl9___NzLlUFc_RQafCT1_rTSFeqmVteLLHT-mh3DWVkn9LtfFJw_Qo4kgKr8OZHDqNnuwg612sGyflFn1du3SKTgOviL1pi6btrsOFhVUMNIsMkAGYM_6gLIfS_QA; sdidtest0UnHUgv0_qmfD4KAKlwzhqZb5S6OXLz9qXc5mn5jmM9WUEih_2-hvJz15SCjqFv-OTJvl9___NzLlUFc_RQafCT1_rTSFeqmVteLLHT-mh3DWVkn9LtfFJw_Qo4kgKr8OZHDqNnuwg612sGyflFn1du3SKTgOviL1pi6btrsOFhVUMNIsMkAGYM_6gLIfS_QA; PHPSESSIDrk1l8j34e25mvqsd97les1e2k0; undefinedundefined; huya_flash_rep_cnt4; huya_web_rep_cnt35}
# 创建请求对象
req Request(url,headersheader)
# 发送请求
resp urlopen(req)
# 获取响应信息
with open(myblog.html,w,encodingutf-8) as f:f.write(resp.read().decode(utf-8))
运行结果如下 3.5、登录后保持cookie
概述为实现Cookie不丢失可以通过urllib.request.HTTPCookieProcessor来扩展opener的功能。
由于目前学到的登录方式无法登录绝大多数网站下面贴出参考代码以供参考
from urllib.request import Request,build_opener
from fake_useragent import UserAgent
# 用于转码
from urllib.parse import urlencode
from urllib.request import HTTPCookieProcessor# 登录界面地址
login_url https://www.kuaidaili.com/login/# 用于登录的参数
args {username:398707160qq.com,passwd:123456abc
}# 请求头
headers {User-Agent:UserAgent().chrome
}# 创建请求对象封装请求参数
req Request(login_url,headers headers,data urlencode(args).encode())# 创建一个可以保存cookie的控制器对象
handler HTTPCookieProcessor()# 构造发送请求的对象
opener build_opener(handler)# 登录
resp opener.open(req)
-------------------------上面已经登录好----------------------------------# 登录成功后的页面地址
index_url https://www.kuaidaili.com/usercenter/overview
index_req Request(index_url,headers headers)
index_resp opener. Open(index_req)
with open(test.html,wb,encodingutf-8) as f:f.write(index_resp.read())3.6、请求异常处理
概述当程序访问的资源路径存在错误时错误情况又分为根目录正确 与 根目录错误两种情况它们的状态码路径存在差异可通过调试程序了解。
from urllib.request import Request,urlopen
from fake_useragent import UserAgent
from urllib.error import URLError# 请求地址和请求头信息
url https://baidu.com/abcdefg
header {user-agent:UserAgent().chrome}
# 封装请求参数
req Request(url,headersheader)try:# 发送请求后返回的结果resp urlopen(req)# 打印响应信息print(resp.read().decode()[:1000])
except URLError as e:print(状态码如下:)if e.args:# e.args[0].errno是通过调试知道的# 当请求的根目录不存在时就抛出这个状态码print(e.args[0].errno)else:# 如果请求的根目录是对的就会抛出这个状态码print(e.code)print(程序结束) 4、Requests模块
概述Requests属于第三方模块主要用于发送请求并且发送请求时的参数无需转码。
4.1、Requests模块的基本使用
安装(在Pycharm的 终端[ALTF12] 中输入以下命令)
pip install requests
运行结果如下 requests的基本请求方式
req requests.get(http://www.baidu.com)
req requests.post(http://www.baidu.com)
req requests.put(http://www.baidu.com)
req requests.delete(http://www.baidu.com)
req requests.head(http://www.baidu.com)
req requests.options(http://www.baidu.com) 知识点补充urllib发送请求与requests发送请求最大的区别在于urllib的参数需要通过urlencode()进行转码但requests的参数无需转码即可直接发送。 实操17尝试通过程序获取百度中有关爬虫的搜索结果(要求通过requests发送get请求获取)
import requests
from fake_useragent import UserAgent# 测试get请求
def Get():# 目标地址url https://www.baidu.com/s# 请求头header {User-Agent:UserAgent().chrome}# 传递的参数param {wd:爬虫}# 发送get请求resp requests.get(url,headersheader,paramsparam)# 打印结果print(resp.text[:1500])if __name__ __main__:Get()
运行结果如下 实操18通过requests向某校教务系统发送post请求并登录
import requests
from fake_useragent import UserAgentdef post():url http://某校教务系统header {User-Agent: UserAgent().chrome}# 构建传递的参数(以下涉及个人隐私信息,所以删除部分内容用.代替)data {csrftoken: ............. .................., .....................,language: zh_CN,yhm: 2........,mm: ...................................................................................................}# 发送请求resp requests.post(url, headersheader, datadata)# 打印结果print(resp.text)if __name__ __main__:post()
运行结果如下 4.2、Requests伪装爬虫
概述在前面的章节中我们已经使用fake_useragent伪装了爬虫的请求头本章节我们将尝试利用Requests为爬虫设置代理IP。
import requests
from fake_useragent import UserAgenturl http://httpbin.org/get
header {User-Agent:UserAgent().edge}def proxy():# 设置IP代理proxy {# 参数格式type : type://ip:porthttp:http://47.243.166.133:18080,}# 发送请求resp requests.get(url,headersheader,proxiesproxy)# 打印响应内容print(resp.text)if __name__ __main__:proxy()
运行结果如下 4.3、Requests功能补充
4.3.1、设置超时时间
概述可以通过timeout属性设置超时时间一旦超过这个时间还没获得响应内容就会提示错误。
requests.get(http://github.com, timeout0.5) 4.3.2、session自动保存cookies
概述seesion的意思是保持一个会话比如 登陆后继续操作(记录身份信息) 而requests是单次请求的请求身份信息不会被记录。
# 创建一个session对象
s requests.Session()
# 用session对象发出get请求设置cookies
s.get(http://httpbin.org/cookies/set/sessioncookie/123456789) 4.3.3、SSL验证
概述当网站报错SSL错误时可通过以下代码解决。
# 禁用安全请求警告
requests.packages.urllib3.disable_warnings()
resp requests.get(url, verifyFalse, headersheaders)4.3.4、获取响应信息
代码含义resp.json()获取响应内容以json字符串resp.text获取响应内容 (以字符串)resp.content获取响应内容以字节的方式resp.headers获取响应头内容resp.url获取访问地址resp.encoding获取网页编码resp.request.headers请求头内容resp.cookie获取cookie 实操20尝试登录快代理网站利用session保存cookie。(由于技术受限目前该网站已经加入了滑块验证因此无法实现post请求所以拿到的界面是没有登录的界面但是大家可以借鉴实操中利用requests保存cookie的思路)
import requests
from fake_useragent import UserAgenturl1 https://youtube.com/
header {User-Agent:UserAgent().edge}def timeout():resp requests.get(url1,timeout0.8,headersheader)print(f网站编码为{resp.encoding})print(f获取的网页内容为\n{resp.text})def session():url_login https://www.kuaidaili.com/login/data {login_type: 1,username: 17........,passwd: ........,next: /}url_index http://www.kuaidaili.com/usercenter/overview/# 使用session对象保存cookie并发送post请求实现登录s requests.Session()s.post(url_login,headersheader,datadata)# 发送get请求验证session是否保存了cookieresp s.get(url_index,headersheader)with open(myText.html, w,encodingutf-8) as f:f.write(resp.text)if __name__ __main__:session()5、数据解析
5.1、正则表达式
概述正则表达式是对字符串操作的一种逻辑公式就是用事先定义 好的一些特定字符、及这些特定字符的组合组成一个“规则字 符串”这个“规则字符串”用来表达对字符串的一种过滤逻辑。 5.1.1、匹配规则
定位符
字符描述^匹配输入字符串开始的位置。如果设置了 RegExp 对象的 Multiline 属性^ 还会与 \n 或 \r 之后的位置匹配。$匹配输入字符串结尾的位置。如果设置了 RegExp 对象的 Multiline 属性$ 还会与 \n 或 \r 之前的位置匹配。\b匹配一个单词边界即字与空格间的位置。\B非单词边界匹配。
注意定位符与限定符不能同时使用。 普通字符
字符描述[ABC]匹配 [...] 中的所有字符例如 [aeiou] 匹配字符串 google roobit muxi 中所有的a e o u a 字母[^ABC]匹配除了 [...] 中字符的所有字符例如 [^aeiou] 匹配字符串 google runoob taobao 中除了a e o u a 字母的所有字母[A-Z][A-Z] 表示一个区间匹配所有大写字母[a-z] 表示所有小写字母.匹配除换行符\n、\r之外的任何单个字符相等于 [^\n\r][\s\S]匹配所有字符。\s 是匹配所有空白符包括换行\S 非空白符不包括换行\w匹配字母、数字、下划线。等价于 [A-Za-z0-9_] 特殊字符
特别字符描述( )标记一个子表达式的开始和结束位置一般用于分组。子表达式可以获取供以后使用。要匹配这些字符请使用 ( 和 )。[标记一个中括号表达式的开始。要匹配 [请使用 \[。?匹配前面的子表达式零次或一次或指明一个非贪婪限定符。要匹配 ? 字符请使用 \?。\将下一个字符标记为或特殊字符、或原义字符、或向后引用、或八进制转义符。例如 n 匹配字符 n。\n 匹配换行符。序列 \ 匹配 而 ( 则匹配 (。{标记限定符表达式的开始。要匹配 {请使用 \{。|指明两项之间的一个选择。要匹配 |请使用 \|。 非打印字符
字符描述\cx匹配由x指明的控制字符。例如 \cM 匹配一个 Control-M 或回车符。x 的值必须为 A-Z 或 a-z 之一。否则将 c 视为一个原义的 c 字符。\f匹配一个换页符。等价于 \x0c 和 \cL\n匹配一个换行符。等价于 \x0a 和 \cJ。\r匹配一个回车符。等价于 \x0d 和 \cM\s匹配任何空白字符包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]。注意 Unicode 正则表达式会匹配全角空格符。\S匹配任何非空白字符。等价于 [^ \f\n\r\t\v]\t匹配一个制表符。等价于 \x09 和 \cI\v匹配一个垂直制表符。等价于 \x0b 和 \cK 限定符
字符描述*匹配前面的子表达式零次或多次。例如zo* 能匹配 z 以及 zoo。* 等价于{0,}。匹配前面的子表达式一次或多次。例如zo 能匹配 zo 以及 zoo但不能匹配 z。 等价于 {1,}。?匹配前面的子表达式零次或一次。例如do(es)? 可以匹配 do 、 does 中的 does 、 doxy 中的 do 。? 等价于 {0,1}。{n}n 是一个非负整数。匹配确定的 n 次。例如o{2} 不能匹配 Bob 中的 o但是能匹配 food 中的两个 o。{n,}n 是一个非负整数。至少匹配n 次。例如o{2,} 不能匹配 Bob 中的 o但能匹配 foooood 中的所有 o。o{1,} 等价于 o。o{0,} 则等价于 o*。{n,m}m 和 n 均为非负整数其中n m。最少匹配 n 次且最多匹配 m 次。例如o{1,3} 将匹配 fooooood 中的前三个 o。o{0,1} 等价于 o?。请注意在逗号和两个数之间不能有空格。 5.1.2、贪婪模式与非贪婪模式
概述正则表达式通常用于在文本中查找匹配的字符串Python里数量词默认是贪婪的在少数语言里也可能是默认非贪婪总是尝试匹配尽可能多的字符 非贪婪的则相反总是尝试匹配尽可能少的字符。
举例正则表达式”ab”如果用于查找”abbbc”将找到”abbb”。如果使用非贪婪的数量词”ab?”将找到”a” 5.1.3、正则表达式的修饰符(可选标志)
概述正则表达式可以包含一些可选标志修饰符来控制匹配的模式并且多个标志可以通过按位 OR(|) 来连接。
修饰符描述re.I使匹配对大小写不敏感re.L做本地化识别locale-aware匹配re.M使边界字符 ^ 和 $ 匹配每一行的开头和结尾记住是多行而不是整个字符串的开头和结尾re.S使 . 匹配包括换行在内的所有字符re.U根据Unicode字符集解析字符。这个标志影响 \w, \W, \b, \Bre.X该标志通过给予你更灵活的格式以便你将正则表达式写得更易于理解 正则表达式参考网站正则表达式在线测试 | 菜鸟工具 (jyshare.com) 5.2、Python操作正则表达式
5.2.1、常用方法
参数说明
pattern表示要匹配的内容是一个正则表达式
string表示用于匹配的字符串
replace表示要替换的内容。
# re.match是尝试从字符串的起始位置开始匹配的一个模式如果不是起始位置匹配成功的话match()就返回none
re.match(pattern, string)
# re.search 扫描整个字符串并返回第一个成功匹配的结果
re.search(pattern, string)
# re.findall 查找全部匹配的内容
re.findall(pattern,string)
# re.sub用于替换字符串
re.sub(pattern,replace,string)
实操21练习所有Python操作正则表达式的方法。
import re
# 用于匹配的字符串
my_str python is the best programming language in the worldprint(-----------------------match(规则,内容)测试---------------------------------)
# 精准匹配字符串是否是以p开头
m1 re.match(p,my_str)
print(m1)
print(fm1匹配的内容为:{m1.group()})# 匹配my_str字符串中从头开始最多2个字符(字符包括字母数字和下划线)
m2 re.match(\w{,2},my_str)
print(m2)
print(fm2匹配的内容为:{m2.group()})# 匹配my_str字符串中从头开始最多十个非空字符的内容
m3 re.match(\S{,10},my_str)
print(m3)
print(fm3匹配的内容为:{m3.group()})print(-----------------------search(规则,内容)测试---------------------------------)
# 匹配my_str字符串中第一个以i开头除字母i外后面的字母最大长度为5的单词
s1 re.search(i\w{,5},my_str)
print(fs1匹配的内容为:{s1.group()})# 匹配字符串中第一个单词
s2 re.search(\w,my_str)
print(fs2匹配的内容为:{s2.group()})# 匹配字符串中第二个单词
s3 re.search(\s\w,my_str)
print(fs3匹配的内容为:{s3.group()})# 匹配字符串中第三个单词(运用分组)
s4 re.search(\s\w(\s\w),my_str)
print(fs4匹配的内容为:{s4.group(1)})print(-----------------------findall(规则,内容)测试---------------------------------)
# 匹配my_str字符串中所有以i开头的单词findall如果没有匹配项就会返回一个空列表
f1 re.findall(\si\w,my_str)
f2 []
# 取出多余空格
for i in f1:j str(i).replace( ,)f2.append(j)
print(ff1匹配的内容为:{f1})
print(ff1去掉多余空格后的内容为:{f2})# 匹配new_str字符串中的网址和网址对应的文本内容
# 如果findall方法中有分组那么该方法就会将所有分组匹配的内容返回
new_str htmldiva hrefhttps://www.baidu.com/百度一下/a/div/html
# .表示除换行外的任意字符表示1次或多次
f3 re.findall(a href(.)(\w)/a,new_str)
print(ff3匹配的内容为:{f3})print(-----------------------sub(规则(要替换的内容),替换后的内容,内容(要替换的内容来自哪个字符串))测试---------------------------------)
# 将所有的python替换成Python
sub1 re.sub(python,Python,my_str)
print(fmy_str替换后的内容为:{sub1})
运行结果如下 5.2.2、正则表达式实战
实操22对某静态新闻网站进行爬取要求过滤出所有有关关键词湖南的网址及其对应的标题。
import re
import requests
from fake_useragent import UserAgenturl http://www.cdyee.com/
# 创建请求头
header {User-Agent:UserAgent().edge}# 禁用安全请求警告并发送请求
requests.packages.urllib3.disable_warnings()
resp requests.get(url, verifyFalse, headersheader)# 设置字符集(防止内容乱码)
resp.encodingutf-8f re.findall(a . href(.)div.(.*湖南.*)/div/a,resp.text)print(f所有有关湖南的新闻链接及其标题有:{f})
运行结果如下 5.3、bs4
5.3.1、bs4入门
概述
Beautiful Soup提供一些简单的、python式的函数用来处理导航、搜索、修改分析树等功能。它是一个工具箱通过解析文档为用户提供需要抓取的数据因为简单所以不需要多少代码就可以写出一个完整的应用程序。
Beautiful Soup自动将输入文档转换为Unicode编码输出文档转换为utf-8编码。不需要考虑编码方式除非文档没有指定一个编码方式这时Beautiful Soup就不能自动识别编码方式了。然后仅仅需要说明一下原始编码方式就可以了。
Beautiful Soup已成为和lxml、html6lib一样出色的python解释器为用户灵活地提供不同的解析策略或强劲的速度。
bs4官网Beautiful Soup 4.4.0 文档 — beautifulsoup 4.4.0q 文档
bs4模块安装pycharm终端(ALTF12)中运行
# 下面两种方式任选其一即可
pip install beautifulsoup4
pip install bs4
运行结果如下 知识点补充Beautiful Soup支持Python标准库中的HTML解析器,还支持一些第三方的解析器如果我们不安装它则 Python 会使用 Python默认的解析器与默认解析器相比之下lxml 解析器更加强大速度更快推荐安装。
解析器使用方法优势劣势Python标准库BeautifulSoup(markup, “html.parser”)1. Python的内置标准库 2. 执行速度适中 3.文档容错能力强Python 2.7.3 or 3.2.2)前 的版本中文档容错能力差lxml HTML 解析器BeautifulSoup(markup, “lxml”)1. 速度快 2.文档容错能力强需要安装C语言库lxml XML 解析器BeautifulSoup(markup, [“lxml”, “xml”]) BeautifulSoup(markup, “xml”)1. 速度快 2.唯一支持XML的解析器 3.需要安装C语言库html5libBeautifulSoup(markup, “html5lib”)1. 最好的容错性 2.以浏览器的方式解析文档 3.生成HTML5格式的文档 4.速度慢不依赖外部扩展
lxml解析器安装pycharm终端(ALTF12)中运行
pip install lxml运行结果如下 创建bs4对象
from bs4 import BeautifulSoup
# html代表要解析的内容lxml表示使用lxml解析器进行解析
BeautifulSoup(html,lxml)
实操23尝试利用bs4获取html代码中的标签信息。
from bs4 import BeautifulSouphtml
title百度/title
div floatleftWelcome to Baidu/div
div classinfo floatrightspan开始搜索/spana hrefwww.baidu.com target_blank百度一下/astrong!--注释信息--/strong
/div
bs BeautifulSoup(html,lxml)
print(-----------------获取标签-----------------)
# 获取title标签的内容
print(bs.title)
# 获取div标签的内容(html中有两个div标签但只能获取到第一个div标签这也是bs4的缺点)
print(bs.div)
# 获取div标签内部的strong标签(即使是内部标签bs4也会逐层查找)
print(bs.strong)print(-----------------获取属性-----------------)
# 获取第一个div容器的所有属性
print(bs.div.attrs)
# 获取a标签的href属性对应的值(下面两种写法都可)
print(fa标签的href属性对应的值为:{bs.a.get(href)})
print(fa标签的href属性对应的值为:{bs.a[href]})print(-----------------获取内容-----------------)
# 获取title包含的文本内容(下面两种写法都可)
print(bs.title.string)
print(bs.title.text)# 下面区分string和text(string会提取所有内容text会解析内容后再提取)
print(f通过string获取strong标签中的注释信息:{bs.strong.string})
print(f通过text获取strong标签中的注释信息:{bs.strong.text}) # 由于strong标签的内容是html中的注释标签所以不会打印内容
运行结果如下 5.3.2、bs4进阶
5.3.2.1、find_all() 搜索文档树
①、当传入的值是字符串
概述传入一个字符串参数Beautiful Soup会查找与字符串完整匹配的内容。
#返回所有的div标签
print(soup.find_all(div)) ②、当传入的值是正则表达式
概述传入正则表达式作为参数Beautiful Soup会通过正则表达式的 match() 来匹配内容。
#返回所有的div标签
print (soup.find_all(re.compile(^div)))③、当传入的值是列表
概述传入列表参数Beautiful Soup会将与列表中任一元素匹配的内容返回。
#返回所有匹配到的span a标签
print(soup.find_all([span,a]))④、当传入的值是一个id的参数
概述传入一个 id 的参数Beautiful Soup会搜索每个标签的 id 对应的属性。
#返回id为welcom的标签
print(soup.find_all(idwelcome))⑤、当传入的值为True
概述True 可以匹配任何值表示获取所有的标签。
print(soup.find_all(True)) ⑥、按CSS进行搜索
概述通过 class_ 参数搜索所有指定CSS类名的标签。
# 返回class等于info的div
print(soup.find_all(div,class_info))⑦、按属性进行搜索
概述通过指定容器容器中的属性及其对应的值精确找到指定标签。
# 返回class属性为info的div(这里的容器,属性及其对应的值都可以进行更换))
soup.find_all(div, attrs{class: info})5.3.2.1、CSS选择器
语法
soup.select(参数)
参数
表达式说明tag选择指定标签*选择所有节点#id选择id为container的节点.class选取所有class包含container的节点li a选取所有li下的所有a节点ul p(兄弟)选择ul后面的第一个p元素div#id ul(父子)选取id为id的div的第一个ul子元素table ~ div选取与table相邻的所有div元素a[title]选取所有有title属性的a元素a[classtitle]选取所有class属性为title值的aa[href*http]选取所有href属性包含muxi的a元素a[href^http]选取所有href属性值以http开头的a元素a[href$.png]选取所有href属性值以.png结尾的a元素input[typeredio]:checked选取选中的hobby的元素 实操24练习bs4中find_all和select方法检索指定信息。
from bs4 import BeautifulSouphtml
title idname百度/title
div floatleft classinfoWelcome to Baidu/div
div classinfo floatright idrightspan开始搜索/spana hrefwww.baidu.com target_blank百度一下/astrong classmuxi!--注释信息--/strong
/div
bs BeautifulSoup(html,lxml)print(---------------------获取整个标签---------------------)
print(---------------------find_all()---------------------)
# 通过结果可知find_all()不仅会找到所有的div标签还会将其子标签一起获取
print(f获取html中所有的div标签:{bs.find_all(div)})
print()# 获取所有id的属性值为name的标签(通过id值进行搜索)
print(fid为name的标签:{bs.find_all(idname)})
print()# 获取class的属性值为class的标签(通过css进行搜索)
print(fclass属性为info的标签为:{bs.find_all(class_info)})
print()# 获取所有target属性为_blank的标签(通过属性进行搜索这里没有指定标签类型表示所有的标签)
print(ftarget属性为_blank的标签为:{bs.find_all(attrs{target:_blank})})
print()print(---------------------CSS选择器---------------------)
# 通过CSS选择器获取strong标签
print(bs.select(strong))
print()# 获取id为right的div标签下的第一个span标签
print(fid为right的div标签下的第一个span标签:{bs.select(div#right span)})
print()# 获取class为muxi的标签
print(fclass为muxi的标签为:{bs.select(.muxi)})
print()print(---------------------获取具体的内容(和bs4基础章节的用法类似)---------------------)
# 获取id为right的div标签下的第一个span标签的文本信息由于获取整个标签时返回的类型是列表所以获取内容时要指定下标
print(fid为right的div标签下的第一个span标签的文本信息为:{bs.select(div#right span)[0].text})
# 获取a标签的链接(下面两种方法都可)
print(fa标签的链接为:{bs.select(a)[0][href]})
print(fa标签的链接为:{bs.select(a)[0].get(href)})
运行结果如下 5.3.3、bs4实操
实操25获取某新闻网站的新闻标题及其对应的链接。
import requests
from bs4 import BeautifulSoup
from fake_useragent import UserAgent# 准备目标地址和请求头
url http://www.people.com.cn/
header {User-Agent:UserAgent().edge}# 发送请求
resp requests.get(url,headersheader)# 设置字符集
resp.encodinggbk# 创建bs4对象
bs BeautifulSoup(resp.text,lxml)# 提取数据
dic {}
for i in range(len(bs.select(ul.list1 li a))):dic[bs.select(ul.list1 li a)[i].text] bs.select(ul.list1 li a)[i].get(href)
print(dic)
运行结果如下 5.4、pyquery
官网pyquery: a jquery-like library for python — pyquery 1.2.4 documentation
安装pycharm终端(ALTF12)中运行
pip install pyquery运行结果如下 5.4.1、初始化方式
①、当要解析的内容是字符串时 from pyquery import PyQuery as pq# 创建一个pyquery对象doc pq(str)# 查找名为tagname的标签print(doc(tagname))②、当要解析的内容是url时 from pyquery import PyQuery as pq# 创建pyquery对象,该语句会发送一个 HTTP 请求获取该网页的 HTML 内容并将其解析成一个可操作的文档对象doc pq(urlhttp://www.baidu.com)# 在解析后的文档中查找 title 标签的内容并将其打印出来print(doc(title)) ③、当要解析的内容是文件时 from pyquery import PyQuery as pqdoc pq(filenamedemo.html)print(doc(tagname))5.4.2、选择节点
①、选择当前节点 from pyquery import PyQuery as pqdoc pq(filenamedemo.html)doc(#main #top)②、选择子节点
思路
1、在doc中一层层写出来
2、获取到父标签后使用children方法 from pyquery import PyQuery as pqdoc pq(filenamedemo.html)doc(#main #top).children() ③、选择父节点
思路到当前节点后使用parent方法 ④、获取兄弟节点
思路获取到当前节点后使用siblings方法 5.4.3、获取属性 from pyquery import PyQuery as pqdoc pq(filenamedemo.html)div doc(#main #top)# 注意:下面的a并不是变量而是demo.html文件中的标签aprint(a.html())# 需要提前使用type()方法获取其类型,然后调用对应的方法获取属性print(a.text()) # pyqueryprint(a.text) #element 5.4.4、获取内容 from pyquery import PyQuery as pqdoc pq(filenamedemo.html)div doc(#main #top)# 注意:下面的a并不是变量而是demo.html文件中的标签aprint(a.html())print(a.text()) # pyqueryprint(a.text) #element 5.5、xpath
5.5.1、xpath入门
概述之前 BeautifulSoup 的用法已经是非常强大的库了不过还有一些比较流行的解析库例如 lxml使用的就是 Xpath 语法同样是效率比较高的解析方法。XPath 是一门在 XML 文档中查找信息的语言。XPath 可用来在 XML 文档中对元素和属性进行遍历。XPath 是 W3C XSLT 标准的主要元素并且 XQuery 和 XPointer 都构建于 XPath 表达之上 参考网站
官网 lxml - Processing XML and HTML with Python
w3c XPath 教程 节点关系
①、父节点Parent
②、子节点Children
③、同胞节点Sibling同级节点。
④、先辈节点Ancestor父节点的父节点(至少是爷爷辈还可以有更深的层次关系)。
⑤、后代节点Descendant子节点的子节点(至少是孙子辈还可以有更深的层次关系)。 常用路径表达式
表达式描述nodename(标签名)选取此节点(标签)的所有子节点/从根节点选取//从匹配选择的当前节点选择文档中的节点而不考虑它们的位置(从任意位置选取).选取当前节点..选取当前节点的父节点选取属性 通配符
概述XPath 通配符可用来选取未知的 XML 元素。
通配符描述举例结果*匹配任何元素节点xpath(div/*)获取div下的所有子节点匹配任何属性节点xpath(div[*])选取所有带属性的div节点node()匹配任何类型的节点 选取若干路径
概述通过在路径表达式中使用“|”运算符您可以选取若干个路径
表达式结果xpath(//div|//table)获取所有的div与table节点 谓语
概述谓语被嵌在方括号内用来查找某个特定的节点或包含某个制定的值的节点。
表达式结果xpath(/body/div[1])选取body下的第一个div节点xpath(/body/div[last()])选取body下最后一个div节点xpath(/body/div[last()-1])选取body下倒数第二个节点xpath(/body/div[positon()3])选取body下前两个div节点xpath(/body/div[class])选取body下带有class属性的div节点xpath(/body/div[classmain])选取body下class属性为main的div节点xpath(/body/div[price35.00])选取body下price元素大于35的div节点 运算符
运算符描述实例返回值计算两个节点集//book//cd加法6 410–减法6 – 42*乘法6 * 424div除法8 div 42等于price9.80如果 price 是 9.80则返回 true。如果 price 是 9.90则返回 false。!不等于price!9.80如果 price 是 9.90则返回 true。如果 price 是 9.80则返回 false。小于price9.80如果 price 是 9.00则返回 true。如果 price 是 9.90则返回 false。小于或等于price9.80如果 price 是 9.00则返回 true。如果 price 是 9.90则返回 false。大于price9.80如果 price 是 9.90则返回 true。如果 price 是 9.80则返回 false。大于或等于price9.80如果 price 是 9.90则返回 true。如果 price 是 9.70则返回 false。or或price9.80 or price9.70如果 price 是 9.80则返回 true。如果 price 是 9.50则返回 false。and与price9.00 and price9.90如果 price 是 9.80则返回 true。如果 price 是 8.50则返回 false。mod计算除法的余数5 mod 21 xpath筛选xml文件的节点
①、element元素节点/元素节点名(例如/div用于获取所有的div)
②、attribute属性节点属性名
③、text() 文本节点/text()
④、concat(元素节点,元素节点)用于连接两个内容
⑤、comment 注释节点
⑥、root 根节点 5.5.2、xpath工具
①、浏览器 - 元素 - Ctrl F
概述首先打开浏览器开发工具(F12)点击元素选项卡再按CtrlF快捷键即可打开xpath工具然后在xpath工具中输入xpath语句即可(例如查找所有class属性值为book-mid-info的div下的h2下的a标签//div[classbook-mid-info]/h2/a)。 ②、浏览器 - 控制台 - $x(xpath表达式)
概述首先打开浏览器开发工具(F12)点击控制台选项卡然后在控制台中输入$x()在小括号中写入xpath语句最后敲回车即可。 ③、浏览器插件之XPath Helper
概述需要通过浏览器设置 - 扩展 - 获取扩展 - 搜索XPath Helper - 安装XPath Helper走完以上流程后才能使用点击安装后的XPath Helper在左侧数据xpath语句右侧就会显示对应的内容。 注意如果想要在python中使用xpath语句需要通过如下语句提前在终端(AltF12)下载lxml
pip install lxml 5.5.3、xpath实战
实操26获取某小说网站中不同排行榜的书名及其对应的网络地址。
import requests
from fake_useragent import UserAgent
from lxml import etree
url https://www.faloo.com/
header {User-Agent:UserAgent().edge}resp requests.get(url,headersheader)
resp.encoding gbk# 创建etree对象
e etree.HTML(resp.text)
fic_name e.xpath(//div[classbangBox]/ul/li/a/title)
fic_url e.xpath(//div[classbangBox]/ul/li/a/href)# 创建一个空字典方便后面做去重
fiction {}for name,url in zip(fic_name,fic_url):# 对应书名和url地址并去重fiction[name] url
print(fiction)
运行结果如下 5.6、JSON数据
概述JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式它使得人们很容易的进行阅读和编写。同时也方便了机器进行解析和生成。适用于进行数据交互的场景比如网站前台与后台之间的数据交互JSON和XML的比较可谓不相上下。Python 中自带了JSON模块直接import json就可以使用了。 参考网站 官方文档json — JSON encoder and decoder — Python 3.12.4 documentation Json在线解析网站JSON在线解析及格式化验证 - JSON.cn 理解json简单说就是javascript中的对象和数组所以这两种结构就是对象和数组两种结构通过这两种结构可以表示各种复杂的结构。
对象对象在js中表示为{ }括起来的内容数据结构为 { keyvalue, keyvalue, ... }的键值对的结构。在面向对象的语言中key为对象的属性value为对应的属性值。取值方法为 对象.key 获取属性值这个属性值的类型可以是数字、字符串、数组、对象这几种
数组数组在js中是中括号[ ]括起来的内容数据结构为 [Python, javascript, C, ...]取值方式和所有语言中一样使用索引获取字段值的类型可以是 数字、字符串、数组、对象几种 Python中的json模块
概述json模块提供了四个功能dumpsdumploadsload。
json.loads()
概述json.loads() 的主要作用是将 JSON 格式的数据转换为 Python 中的字典、列表等数据类型。
注意用 json.loads() 解析 {city: 北京, name: 范爷} 时键一定要用双引号包裹否则无法解析并报错。
import jsonstrList [1, 2, 3, 4]
strDict {city: 北京, name: 范爷}
json.loads(strList)
# [1, 2, 3, 4]
json.loads(strDict) # json数据自动按Unicode存储
# {ucity: u\u5317\u4eac, uname: u\u5927\u732b} json.dumps()
概述json.dumps() 主要作用是将 Python 的数据结构如字典、列表等转换为 JSON 格式的字符串以便存储或传输。
import jsonlistStr [1, 2, 3, 4]
tupleStr (1, 2, 3, 4)
dictStr {city: 北京, name: 范爷}json.dumps(listStr)
# [1, 2, 3, 4]
json.dumps(tupleStr)
# [1, 2, 3, 4]# 注意json.dumps() 序列化时默认使用的ascii编码
# 添加参数 ensure_asciiFalse 禁用ascii编码按utf-8编码json.dumps(dictStr)
# {city: \\u5317\\u4eac, name: \\u5927\\u5218}# ensure_ascii表示不转换成unicode编码,原文显示
print(json.dumps(dictStr, ensure_asciiFalse))
# {city: 北京, name: 范爷} json.dump()
概述json.dump() 的主要作用是将 Python 数据结构如字典、列表等转换为 JSON 格式并保存到文件中。
import jsonlistStr [{city: 北京}, {name: 范爷}]
json.dump(listStr, open(listStr.json,w), ensure_asciiFalse)
# ensure_ascii表示不转换成unicode编码,原文显示
dictStr {city: 北京, name: 范爷}
json.dump(dictStr, open(dictStr.json,w), ensure_asciiFalse) json.load()
概述json.load() 的主要作用是将存储在文件中的 JSON 数据转换为 Python 中的字典、列表等数据类型。
import jsonstrList json.load(open(listStr.json))
print(strList)# [{ucity: u\u5317\u4eac}, {uname: u\u5927\u5218}]strDict json.load(open(dictStr.json))
print(strDict)
# {ucity: u\u5317\u4eac, uname: u\u5927\u5218} 5.7、jsonpath
概述JsonPath 是一种信息抽取类库是从JSON文档中抽取指定信息的工具提供多种语言实现版本包括PythonJavascript PHP 和 Java。JsonPath 对于 JSON 来说相当于 XPATH 对于 XML。 安装pycharm终端(ALTF12)中运行
pip install jsonpath
运行结果如下 参考文档JSONPath - XPath for JSON JsonPath与XPath语法对比
概述Json结构清晰可读性高复杂度低非常容易匹配下表中对应了XPath的用法
XPathJSONPath描述/$根节点.现行节点/.or[]取子节点..n/a取父节点Jsonpath未支持//..就是不管位置选择所有符合条件的条件**匹配所有元素节点n/a根据属性访问Json不支持因为Json是个Key-value递归结构不需要。[][]迭代器标示可以在里边做简单的迭代操作如数组下标根据内容选值等\|[,]支持迭代器中做多选。[]?()支持过滤操作.n/a()支持表达式计算()n/a分组JsonPath不支持 实操27获取某招聘网站的所有城市信息该网站的数据是json格式。
import requests
from fake_useragent import UserAgent
import json
from jsonpath import jsonpath# 准备目标网址和请求头
url http://www.lagou.com/lbs/getAllCitySearchLabels.json
header {User-Agent: UserAgent().edge,}# 创建会话对象(添加cookie)
session requests.session()# 发送请求
resp session.get(url, headersheader)# 将json类型数据转换成python对象
obj json.loads(resp.text)# 第一个参数是转换成python对象后的数据,第二个参数是表达式(表达式必须以$开始)
name jsonpath(obj,$..name)city []
for i in name:city. Append(i)
print(city)
运行结果如下 5.8、数据提取巩固练习
实操28获取某电影网站中经典影片板块中的片名和评分。
from bs4 import BeautifulSoup
import requests
from fake_useragent import UserAgent# 目标网址
url https://www.maoyan.com/films?showType3# 创建请求头
header {User-Agent:UserAgent().edge}# 创建session对象
session requests.session()# 发送请求
resp session.get(url,headersheader,data{})# 设置字符集
resp.encoding utf-8def bs4_get():# 创建BeautifulSoup对象soup BeautifulSoup(resp.text,lxml)# 创建空字典用于后面存电影名及其对应的评分movie {}# 获取电影名和评分names [name.text for name in soup.select(div.channel-detail.movie-item-title a)]scores [score.text for score in soup.select(div.channel-detail.channel-detail-orange)]# 将电影名和评分都保存到字典中for name,score in zip(names,scores):movie[name] scoreprint(movie)if __name__ __main__:bs4_get()
运行结果如下 实操29利用xpath获取某电影网站中经典影片板块中的电影名称及其对应的评分。
import requests
from fake_useragent import UserAgent
from lxml import etreedef xpath_getdata():# 目标地址url https://www.maoyan.com/films?showType3offset30# 准备请求头header {User-Agent:UserAgent().edge}# 创建会话对象session requests.session()# 发送请求resp session.get(url,headersheader)# 创建etree对象e etree.HTML(resp.text)# 获取电影名names e.xpath(//div[classchannel-detail movie-item-title]/title)# 获取评分(由于网页结构的特殊性xpath不能完美的提取div标签下两个i标签中的文本因此这里将所有获取的div标签中的内容转换为字符串)(div.xpath(string(.))表示返回div标签下所有子节点的串联文本)scores [div.xpath(string(.)) for div in e.xpath(//div[classchannel-detail channel-detail-orange])]movie {}for name,score in zip(names,scores):movie[name] scoreprint(movie)if __name__ __main__:xpath_getdata()
运行结果如下 实操30利用正则表达式获取某电影网站中经典影片板块中的电影名称及其对应的评分。
import requests
from fake_useragent import UserAgent
import redef re_getdata():# 目标地址url https://www.maoyan.com/films?showType3offset60# 创建请求头header {User-Agent:UserAgent().edge}# 创建会话对象session requests.session()# 发送请求resp session.get(url,headersheader)# 解析数据names re.findall(div classchannel-detail movie-item-title title(.?),resp.text)# 将每个存放评分的div获取并将内部的数据传入到get_scores方法中做进一步数据提取(注意正则表达式里的引号要和源程序对应)scores [get_scores(data) for data in re.findall(div classchannel-detail channel-detail-orange(.?)/div,resp.text)]movie {}for name,score in zip(names,scores):movie[name] scoreprint(movie)def get_scores(data):该函数用于进一步确定是否有评分如果有评分就将评分拼接如果没有评分就直接返回原结果if data!暂无评分:data ..join(re.findall(\d,data))return dataif __name__ __main__:re_getdata()
运行结果如下 6、爬虫效率提升
6.1、多线程
6.1.1、多线程原理
概述爬虫使用多线程来处理网络请求使用线程来处理URL队列中的url然后将url返回的结果保存在另一个队列中其它线程再读取这个队列中的数据然后写到文件中 组成部分
①、URL队列和响应队列
概述将要爬取的url放在一个队列中这里使用标准库Queue。
from queue import Queue
urls_queue Queue()
out_queue Queue() ②、多线程的实现方式
概述如果队列为空线程就会被阻塞直到队列不为空。处理队列中的一条数据后就需要通知队列已经处理完该条数据
# 方法一用类包装实现
import threadingclass ThreadCrawl(threading.Thread):def __init__(self, queue, out_queue):threading.Thread.__init__(self)self.queue queueself.out_queue out_queuedef run(self):while True:item self.queue.get()# 方法二利用函数包装实现
from threading import Thread
def func(args)pass
if __name__ __main__:info_html Queue()t1 Thread(targetfunc,args(info_html,))# 方法三:利用线程池管理多线程
# 简单往队列中传输线程数
import threading
import time
import queueclass Threadingpool():def __init__(self,max_num 10):self.queue queue.Queue(max_num)for i in range(max_num):self.queue.put(threading.Thread)def getthreading(self):return self.queue.get()def addthreading(self):self.queue.put(threading. Thread)def func(p,i):time. Sleep(1)print(i)p.addthreading()if __name__ __main__:p Threadingpool()for i in range(20):thread p.getthreading()t thread(target func, args (p,i))t.start() 6.1.2、函数实现爬虫多线程
实操31利用函数实现多线程queue队列实现对某动态网站多页数据的获取解析和保存。
import time
import requests
from fake_useragent import UserAgent
from threading import Thread
from queue import Queue
import jsondef spider():从队列中获取url,然后拿着这个url去获取数据while not url_queue.empty():requests_url url_queue.get()# 获取数据header {User-Agent:UserAgent().firefox}resp requests.request(GET,requests_url,headersheader)# 将获取的数据保存到队列中data_queue.put(resp.text)# 解析数据(从队列中拿json数据并解析成python对象)data_py json.loads(data_queue.get())# 保存数据with open(./myText.html, a, encodingutf-8) as f:f.write(str(data_py).replace(},,},\n))# 降低请求频率time.sleep(4)if __name__ __main__:# 创建url地址的队列url_queue Queue()# 创建数据队列data_queue Queue()# 初始化url,获取前5页的地址for i in range(1,6):url fhttps://www.hupu.com/home/v1/news?pageNo{i}pageSize50# 将产生的url放进url_queue队列中url_queue.put(url)# 创建三个线程for i in range(3):# 创建线程th Thread(targetspider)# 启动线程th.start()
文件内容如下 6.1.3、类实现爬虫多线程
实操32利用类实现多线程queue队列实现对某动态网站多页数据的获取解析和保存。
import time
import requests
from fake_useragent import UserAgent
from threading import Thread
from queue import Queue
import jsonclass MyThread(Thread):def run(self):从队列中获取url,然后拿着这个url去获取数据while not url_queue.empty():requests_url url_queue.get()# 获取数据header {User-Agent:UserAgent().firefox}resp requests.request(GET,requests_url,headersheader)# 将获取的数据保存到队列中data_queue.put(resp.text)# 解析数据(从队列中拿json数据并解析成python对象)data_py json.loads(data_queue.get())# 保存数据with open(./myText.html, a, encodingutf-8) as f:f.write(str(data_py).replace(},,},\n))# 降低请求频率time.sleep(4)if __name__ __main__:# 创建url地址的队列url_queue Queue()# 创建数据队列data_queue Queue()# 初始化url,获取前5页的地址for i in range(1,6):url fhttps://www.hupu.com/home/v1/news?pageNo{i}pageSize50# 将产生的url放进url_queue队列中url_queue.put(url)# 创建三个线程for i in range(3):# 创建线程th MyThread()# 启动线程th.start()
运行结果如下 6.2、多进程
6.2.1、多进程思想
概述multiprocessing是python的多进程管理包和threading.Thread类似。multiprocessing模块可以让程序员在给定的机器上充分的利用CPU在multiprocessing中通过创建Process对象生成进程然后调用它的start()方法。
from multiprocessing import Processdef func(name):print(hello, name)if __name__ __main__:p Process(targetfunc,args(sxt,))p.start()p.join() # 等待进程执行完毕注意在使用并发设计的时候尽可能的避免共享数据尤其是在使用多进程的时候。 如果你真有需要共享数据可以使用由Manager()返回的manager提供list, dict, Namespace, Lock, RLock, Semaphore, BoundedSemaphore, Condition, Event, Barrier, Queue, Value and Array类型的支持。
from multiprocessing import Process,Manager,Lockdef print_num(info_queue,l,lo):with lo:for n in l:info_queue.put(n)def updata_num(info_queue,lo):with lo:while not info_queue.empty():print(info_queue.get())if __name__ __main__:manager Manager()into_html manager.Queue()lock Lock()a [1, 2, 3, 4, 5]b [11, 12, 13, 14, 15]p1 Process(targetprint_num,args(into_html,a,lock))p1.start()p2 Process(targetprint_num,args(into_html,b,lock))p2.start()p3 Process(targetupdata_num,args(into_html,lock))p3.start()p1.join()p2.join()p3.join() 进程池
概述进程池内部维护一个进程序列当使用时则去进程池中获取一个进程如果进程池序列中没有可供使用的进进程那么程序就会等待直到进程池中有可用进程为止。
进程池的方法apply同步执行-串行apply_async异步执行-并行。
from multiprocessing import Pool,Manager
def print_num(info_queue,l):for n in l:info_queue.put(n)def updata_num(info_queue):while not info_queue.empty():print(info_queue.get())if __name__ __main__:html_queue Manager().Queue()a[11,12,13,14,15]b[1,2,3,4,5]pool Pool(3)pool.apply_async(funcprint_num,args(html_queue,a))pool.apply_async(funcprint_num,args(html_queue,b))pool.apply_async(funcupdata_num,args(html_queue,))pool.close() #这里join一定是在close之后且必须要加join否则主进程不等待创建的子进程执行完毕pool.join() # 进程池中进程执行完毕后再关闭如果注释那么程序直接关闭 6.2.2、利用函数实现多进程
实操33利用函数实现多进程Manager队列实现对某动态网站多页数据的获取解析和保存。
import requests
from fake_useragent import UserAgent
from time import sleep
from multiprocessing import Process,Manager
import jsondef spider(url_queue,data_queue):从队列中获取url,然后拿着这个url去获取数据while not url_queue.empty():requests_url url_queue.get()# 获取数据header {User-Agent:UserAgent().firefox}resp requests.request(GET,requests_url,headersheader)# 将获取的数据保存到队列中data_queue.put(resp.text)# 解析数据(从队列中拿json数据并解析成python对象)data_py json.loads(data_queue.get())# 保存数据with open(./myText.html, a, encodingutf-8) as f:f.write(str(data_py).replace(},,},\n))# 降低请求频率sleep(4)if __name__ __main__:# 创建url地址的队列url_queue Manager().Queue()# 创建数据队列data_queue Manager().Queue()# 初始化url,获取前3页的地址for i in range(1,4):url fhttps://www.hupu.com/home/v1/news?pageNo{i}pageSize50# 将产生的url放进url_queue队列中url_queue.put(url)# 创建进程列表all_process []# 创建三个进程for i in range(3):# 创建线程pro Process(targetspider,args(url_queue,data_queue))# 启动线程pro.start()# 将所有进程添加到进程列表中all_process.append(pro)# 等待所有进程结束再关闭进程[p.join() for p in all_process]
运行后的文件内容如下 6.2.3、利用进程池实现多进程
实操34利用进程池Manager队列实现对某动态网站多页数据的获取解析和保存。
import requests
from fake_useragent import UserAgent
from time import sleep
from multiprocessing import Pool,Manager
import jsondef spider(url_queue,data_queue):从队列中获取url,然后拿着这个url去获取数据while not url_queue.empty():requests_url url_queue.get()# 获取数据header {User-Agent:UserAgent().firefox}resp requests.request(GET,requests_url,headersheader)# 将获取的数据保存到队列中data_queue.put(resp.text)# 解析数据(从队列中拿json数据并解析成python对象)data_py json.loads(data_queue.get())# 保存数据with open(./myText.html, a, encodingutf-8) as f:f.write(str(data_py).replace(},,},\n))# 降低请求频率sleep(4)if __name__ __main__:# 创建url地址的队列url_queue Manager().Queue()# 创建数据队列data_queue Manager().Queue()# 初始化url,获取前3页的地址for i in range(1,4):url fhttps://www.hupu.com/home/v1/news?pageNo{i}pageSize50# 将产生的url放进url_queue队列中url_queue.put(url)# 创建进程池,括号内写进程数量pool Pool(3)# 开启异步进程pool.apply_async(funcspider,args(url_queue,data_queue))pool.apply_async(funcspider, args(url_queue, data_queue))pool.apply_async(funcspider, args(url_queue, data_queue))# 关闭所有进程pool.close()# 等待所有进程结束pool. Join()
运行后的文件内容如下 6.3、协程
背景网络爬虫速度效率慢多部分在于阻塞IO这块(网络/磁盘)。在阻塞时CPU的中内核是可以处理别的非IO操作。因此可以考虑使用协程来提升爬虫效率这种操作的技术就是协程。
概述协程一种轻量级线程拥有自己的寄存器上下文和栈本质是一个进程相对于多进程无需线程上下文切换的开销无需原子操作锁定及同步的开销简而言之就是让阻塞的子程序让出CPU给可以执行的子程序。
注意一个进程包含多个线程一个线程可以包含多个协程多个线程相对独立线程的切换受系统控制。 多个协程也相对独立但是其切换由程序自己控制
安装在Pycharm终端(AltF12)中运行如下代码
pip install aiohttp
运行结果如下 参考网站Welcome to AIOHTTP — aiohttp 3.9.5 documentation
常用方法
属性或方法功能aiohttp.ClientSession()获取客户端函数session.get(url)发送get请求seesion.post(url)发送post请求resp.status获取响应状态码resp.url获取响应url地址resp.cookies获取响应cookie内容resp.headers获取响应头信息resp.read()获取响应bytes类型resp.text()获取响应文本内容
实操35利用aiohttp实现协程并展示如何发送请求头和参数。
import aiohttp
import asyncio
from fake_useragent import UserAgent# 定义了一个异步函数
async def header1_aiohttp():该函数用于展示利用aiohttp实现协程时放置请求头的方法1# 创建了aiohttp.ClientSession实例并利用async with保证会话结束后能自动关闭async with aiohttp.ClientSession(headers{User-Agent:UserAgent().edge}) as session:# 利用会话发送get请求并利用async with保证使用完毕后能自动关闭async with session.get(http://httpbin.org/get,) as resp:rs await resp.text()print(rs)async def header2_aiohttp():该函数用于展示利用aiohttp实现协程时放置请求头的方法2async with aiohttp.ClientSession() as session:async with session.get(http://httpbin.org/get,headers{User-Agent:UserAgent().edge}) as resp:rs await resp.text()print(rs)async def params_aiohttp():该函数用于展示利用aiohttp实现协程时如何传递参数async with aiohttp.ClientSession() as session:async with session.get(http://httpbin.org/get,params{key:muxi}) as resp:rs await resp.text()print(rs)if __name__ __main__:# 获取当前线程的事件循环loop asyncio.get_event_loop()# 运行一个异步任务直到括号内的函数执行完成loop.run_until_complete(header1_aiohttp())loop.run_until_complete(header2_aiohttp())loop.run_until_complete(params_aiohttp())
运行结果如下