中国本科高等质量建设研究网站,烟台快速建站公司,个人建站软件公司,无锡装饰网站建设排名爬虫基础 文章目录 爬虫基础爬虫简介爬虫的用途爬虫的合法性规避风险看懂协议反爬机制反反爬策略 认识HTTPHTTP协议--HyperText Transfer ProtocolHTML--HyperText Markup LanguageHTTPS如何查看网站是什么协议呢使用端口号 URL组成部分详解常用的请求- Request Method常见的请…爬虫基础 文章目录 爬虫基础爬虫简介爬虫的用途爬虫的合法性规避风险看懂协议反爬机制反反爬策略 认识HTTPHTTP协议--HyperText Transfer ProtocolHTML--HyperText Markup LanguageHTTPS如何查看网站是什么协议呢使用端口号 URL组成部分详解常用的请求- Request Method常见的请求头参数User-Agent 浏览器名RefererCookie 身份证 常见的响应状态码 - Response Code分析网页静态网页 - 源代码动态网站(异步加载) - 抓包抓包工具ElementsNetwork Requests----get基于www.hao123.com做一个网页采集器 Requests---post基于百度翻译实现文本翻译 基于豆瓣电影获取华语评分前十电影失败原因该产品程序员对网页做出了调整跟着课做不出来数据解析正则表达式匹配单个字符匹配多个字符示例一 ------以中间部分内容示例二 ------以开头^示例三 ----以结尾判定$ 贪婪匹配与非贪婪匹配* 正则表达式的应用案例匹配手机号 1[3-9]\d{9}匹配邮箱 \w[0-9a-z].[a-z]匹配网址 (https:|http:\ftp:)//\S匹配身份证号 \d{17}[xX\d] 正则表达式re模块最低级的字符串处理函数match中级字符串处理函数search高级字符串处理函数findall 也是常用的函数字符串替换函数sub案例 小说筛选器字符串分割函数split换行加注释正则表达式预处理compile 正则表达式的转义字符与原生字符案例 美元$与正则表达式取结尾\$案例 以反斜杠作为标识符案例 匹配网页的标题 正则表达式xPath模块XML Path LanguagexPath节点xPath语法谓语选取未知节点选取若干路径 数据解析xPath-lxml库的使用lxml库lxml库-读取本地xml文件使用HTML的方式读入文件定位元素按标签顺序定位元素按照包含的元素定位/与//的区别python案例---下载人生格言python案例---下载美女图片案例总结 BS4--beautifulsoup4库BS4语法对象的种类获取标签获取标签的名称获取标签的属性/属性值改变标签的属性值获取标签的文本内容获取注释部分的内容遍历文档树搜索文档树CSS选择器 爬虫简介
就是通过代码模拟浏览器上网获取互联网的数据过程
爬虫的用途
爬取网页上的文字、图片、视频、音频等自动填写表单打卡等
爬虫的合法性
在法律中不被禁止具有一定风险不能干扰被访问网站的正常运营----不能高并发式访问网站就是不能1秒内向网上发送上万次请求可能导致服务器崩溃不能抓取受法律保护的特定信息和数据
规避风险
尽量不高并发访问设计敏感内容时及时停止爬取或传播阅读robots.txt协议这上面会说那些数据不能爬取 如何找一个网站的这个协议呢 只需要在那个网址后面加上斜杠和这个名称就好了比如https://www.bilibili.com/robots.txt就可以看到哪些数据不该访问
看懂协议
user-agent 用户代理
Baiduspider 百度爬虫
Disallow 禁止访问一切内容
反爬机制
通过技术手段防止爬虫程序对网站数据进行爬取
对疑似爬虫的ip进行限制
反反爬策略
破解反爬机制获取相关数据
定时切换ip继续获取数据
认识HTTP
HTTP协议–HyperText Transfer Protocol
意思就是超文本发送和接收协议。现在可以传输二进制码流也就是音频图片等等
HTML–HyperText Markup Language
这是一个页面HTTP所要呈现的页面服务器端口是80、
一些小网站啥的浏览器会显示不安全
HTTPS
是加密版的HTTP比HTTP多一个SSL层服务器端口是443。因为是加密过的所以更安全
比如京东啥的浏览器会显示一个锁在网址前面
如何查看网站是什么协议呢
只需要看看网址前缀是什么
使用端口号
在浏览器中的网址后面加上冒号再加上80或者443就能显示页面了
URL组成部分详解
URL是Uniform Resource Locator的简写统一资源定位符。
一个URL由以下几部分组对于 scheme://host:port/path/?query-stringxxx#anchor 来说 scheme:代表的是访问的协议一般为http或者https以及ftp等
就是网址前面的http啥的
host:主机名域名比如www.baidu.com
就是网址后面紧挨着的域名
port:端口号。当你访问一个网站的时候浏览器默认使用80端口
这个端口号浏览器默认添加一般看不见放在域名后面与域名连在一起比如说www.baidu.com:80
path:查找路径。比如:sz.58.com/chuzu/ 后面的/chuzu就是path
这个是你点开初始页面的某个项目这个例子是点开了58租房的一个出租的页面可以把初始页面类比成一个大文件夹然后你点开哪一个页面就是点开哪一个小文件夹
有时候这个类似文件夹的路径是虚拟的
query-string:查询字符串比如:www.baidu.com/s?wdpython后面的wdpython就是查询字符串
这个wd后面的python就是用户想要搜索的信息比如去qq音乐里面搜一个歌手你在搜索框里面填写歌手名实际上是写在了wd的后面然后后端再根据信息返回给你
anchor:锚点前端用来做页面定位的。现在一些前后端分离项目也用锚点来做
因为一个页面有很多板块为了使页面更美观我们会把一些信息分类一类一类的去展示这些板块标上号就可以排版了比如#1是目录#2是歌手大全等等
常用的请求- Request Method
就是你打开一个网站时你向服务器发送请求的方式常用的有两种一个是GET 一个是POST
一共是8种请求方式其他6种与爬虫关系不大
一般来说GET请求我们不会向服务器发送数据不会占用服务器的资源几乎没影响 而POST请求是需要让服务器特意接收我们的信息给我们预留空间会对服务器产生一定的影响
这种都是我们要登录了给服务器提醒或者给服务器上传数据 出来这种信息需要我们登录一下就会出现这个文件了
常见的请求头参数
在http协议中向服务器发送请求时数据分为3种
第一种是放在链接url里面
第二种是把数据放在参数体body里面在post请求中有一部分就在body里面
第三个就是把数据放在请求头里面
User-Agent 浏览器名
就是服务器想知道你这个请求是哪个浏览器发送的 这里面一般有你请求的浏览器版本信息还有你电脑配置的信息
我们以后写爬虫时不填写这个信息人家就会默认你是Python人家后台用最简单的判断就能实现反爬技术人家就会给你一些假数据或者直接拒绝你的请求
为了防止这样我们要模拟自己是一个浏览器自己填写User-Agent所以要经常设置这个值来伪装我们的爬虫
Referer
表明当前请求是从哪个url过来的意思就是我们的想要获取的页面是从哪里来访问的
比如我现在想登录淘宝网那么登录界面就应该从主页点击而来 这里的referer表明我们是从京东主页过来的也就是从京东主页发送请求而来到这个页面
有时候人家也会检查你的referer来判断你来到当前页面是否合法如果不合法就给你反爬
应对这个反爬机制就需要我们自己指定一下referer找指定页面
Cookie 身份证
一般是我们填写账号和密码后服务器会给你一个特定的cookie
服务器通过检查你的cookie来判断你的身份
有cookie之后再访问服务器就不需要登录了就是身份证 一些反爬较强的网站会检测你是否有cookie登录或者不登录都会检查cookie
没用cookie就会拒绝访问
这些参数如何设置请听下回分晓
常见的响应状态码 - Response Code
就是我们向服务器发送请求服务器会返回一个状态响应码 返回 200:请求正常服务器正常的返回数据。 301:永久重定向。比如在访问www.jingdong.com的时候会重定向到www.id.com. 就是你访问的是旧网址人家不用了给你指向新的地址 302临时重定向:比如在访问一个需要登录的页面的时候而此时没有登录那么就会重定向到登录页面。 就像知乎一样知道你没登录就强制跳转到登录页面哪怕你再访问知乎主页还是会被传回登录页面这个传送就是临时重定向 400/404:请求的url在服务器上找不到。换句话说就是请求链接url错误。 403:服务器拒绝访问权限不够或者是被反爬了 500:服务器内部错误。可能是服务器出现bug了或者宕机了。 这个与咱们无关
分析网页
网页分为两类静态网页和动态网页
静态网页 - 源代码
就是计算机向服务器发送请求服务器一次性把所有内容打包发送给我们
像是4399啊或者一些工具网站啊就是那种一次性展示所有的固定信息的网站
我们可以选择源代码的方式获取数据
动态网站(异步加载) - 抓包
就是计算机向服务器请求数据服务器第一次只给框架等计算机接收完之后再次请求服务器才把真正的数据发送过来。然后计算机开始填充内容进行拼接
相当于一次请求分了好几步才给你整个页面
这种方式源代码中一般只有框架不是我们想要的所以我们通过抓包的形式去找数据包 找到的数据包是json文件需要进一步拆分 像这种信息比较全的就是服务器第二次向浏览器发送的数据包了
然后就是浏览器自己拼接了
抓包工具
Elements
帮助分析网页结构有的数据是ajax请求的也就是二次请求就是异步加载的数据包不在源代码里面
Network
查看整个网页发送的所以网络请求
Requests----get
获取静态网页并保存在本地文件中
用浏览器打开这个文件就可以访问这个网页了
基于www.hao123.com做一个网页采集器
下面是作业基于www.hao123.com做一个网页采集器
# https://www.baidu.com/s? 这个网址问号后面的都是数据了
# tn50000021_hao_pg
# ieutf-8scUWd1pgw-pA7EnHc1FMfqnHTzPH64PjTYn1TzPiuW5y99U1Dznzu9m1Ykn1TvnW04njms
# ssl_samplenormal
# srcqid8885165236642971965
# H123Tmpnunew7
# word%E5%91%A8%E6%9D%B0%E4%BC%A6# tn: 50000021_hao_pg
# ie: utf-8
# sc: UWd1pgw-pA7EnHc1FMfqnHTzPH64PjTYn1TzPiuW5y99U1Dznzu9m1Ykn1TvnW04njms
# ssl_sample: normal
# srcqid: 8885165236642971965
# H123Tmp: nunew7
# word: 周杰伦
上面是自己先搜索任意一个关键字得到的网址
然后打开network找包头就是这些data数据tnie等等然后发现word就是我们填充关键字的地方import requests 导入这个模块keyword input(请输入要查询的内容) 索取关键字
url fhttps://www.baidu.com/s?word{keyword} 只需要给个网站域名www..com再给个关键字就好了qingqiu_header {user-agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36 Edg/127.0.0.0} 这是从自己的浏览器复制过来的请求头
这里要把请求头以字典键值对的格式赋值给我们的请求头results requests.get(urlurl,headers qingqiu_header ) 通过查看浏览器的network任意一个文件发现是get的获取方式就用get发送请求参数把我们的网址填进去然后再把请求头填进去模拟浏览器请求
如果不写请求头将返回乱码print(results) -- 200 正常访问
print(results.url) -- 查看我们输入的网址 https://www.baidu.com/s?word%E5%91%A8%E6%9D%B0%E4%BC%A6
print(results.request) ---返回请求的类型比如get/post
print(results.request.headers) --- 查看请求头也就是user-agent如果我们填写了请求头这个结果和请求头是一样的如果没有填写请求头就会返回默认值{User-Agent: python-requests/2.32.3, Accept-Encoding: gzip, deflate, Accept: */*, Connection: keep-alive}发现默认是python直接反爬让我们得到乱码print(results.text) 这里是显示一下你获取的数据以text的格式with open(keyword .html,w,encodingutf-8) as f: 这里是把数据储存在本地文件里面你用浏览器可以打开这个文件f.write(results.text)print(f已下载成功……{keyword}) 告诉用户你已经成功把网页保存起来了
步骤
搜索任意关键词获取网址 www…com找请求头user-agent找关键词对应的接口把关键词和网址以及请求头作为参数使用requests模块检查状态响应码是不是200以text的格式查看返回的内容是真实有效以只写的形式创建文件把网页写进去就保存好了
Requests—post
首先知道什么是异步获取判断是post请求 然后点开负载也就是data数据 不难发现只有一个数据这个数据就是我们输入的文本
也就是以后我们填写关键词的地方
然后就是筛选数据 这是在data值下的第0位元素中v的值
基于百度翻译实现文本翻译
# https://www.hao123.com/
import pprint 格式化模块
import requests 请求模块keyword input(请输入要查询的单词)
url https://fanyi.baidu.com/sugheadersss {user-agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36 Edg/127.0.0.0}data {kw:f{keyword}}ret requests.post(urlurl,headersheadersss,datadata) 就比get多了个data参数# pprint.pprint(ret.json()) 先用这里找数据ret_json ret.json() 超文本转json字典格式
print(str(ret_json[data][0][v])) 最后打印找到的数据
基于豆瓣电影获取华语评分前十电影失败原因该产品程序员对网页做出了调整跟着课做不出来
数据解析
正则表达式xPathBS4
正则表达式
概念 根据某种方法从字符串中找到想要匹配的数据 就是给你篇文章让你找个词圈出来这个词在哪些地方
优点速度快准确度高
缺点新手入门难度大
匹配单个字符
单个字符点 ” . “ 可以匹配任意单字符 1234567我空格两个点 ” … “ 可以两两匹配字符就是 12345678” \d “可以匹配单个数字就是值匹配字符串里面的数字” \d\d “,就是匹配两个相邻的数字比如相约1980年匹配1980 几个斜杠d就是匹配几个数字” \D “匹配非数字内容单个字符” \w “匹配数字字母和下划线” \W “,就是跟小w取反非数字非字母非下划线” \s “,匹配空格、换行符、制表符等等看不见的东西“ \S ”,取反“ [] ”组合匹配将文本与方括号内的的任意字符匹配只要有一个字符能匹配上就行 [a-z] 匹配所有的小写字母[a-zA-Z] 匹配所有的大小写字母[a-zA-Z0-9] 匹配所有的字母和数字[a-zA-Z0-9_$] 匹配括号里面全部的内容字母数字和你输入的特殊符号理论上这个匹配规则可以取代上面所有的\什么东西
匹配多个字符
示例一 ------以中间部分内容
电话:123456
电话:1122334455
电话:123
电话:电话:\d 匹配前三行到数字1的位置电话:\d* 匹配全部数字可以是0个电话:\d 匹配前三个至少一个数字的电话:\d? 匹配0或者1个电话:\d{6} 匹配前两个至少6位数电话的前6位电话:\d{6,} 匹配前两个至少6位数的电话电话:\d{6,8} 匹配前两个位数是6到8的电话
示例二 ------以开头^
112233
123aaa
ab123^\d 匹配以数字开头的字符串 , 前两个的第一位数字^\d 以数字开头的字符串只匹配数字部分第一个的全部第二个的数字部分
示例三 ----以结尾判定$
123.qq.com
www.222.com
666.cncom$ 匹配前两个的com部分 .*com$ 匹配前两个全部 在 Python 的正则表达式中.* 是一个常用的模式具体含义如下 . : 匹配任意字符除了换行符 \n。* : 表示匹配前面的字符在这里是 .0 次或多次。 因此.* 的整体含义是匹配任意数量的任意字符包括零个字符直到遇到不符合匹配条件的字符如果有的话。 ^www.*com$ 组合一下就是以www开头以com结尾的第二个了
贪婪匹配与非贪婪匹配*
对于123456来说
\d* 默认尽量匹配最多,最少是0个也就是123456\d*? 按照*最少的数量匹配也就是不匹配\d 至少匹配一个默认是尽可能多也就是123456\d? 也就是按照的最少数量匹配123456
对于网页的源代码来说比如
h1这是标题h1
.* 匹配全部h1这是标题h1 贪婪模式.*? 匹配h1 非贪婪模式
正则表达式的应用案例
匹配手机号 1[3-9]\d{9} 首先是数字1开头 第二位一般是3到9的其中一个 然后是剩下的9个数字 匹配邮箱 \w[0-9a-z].[a-z] 对于112233168.com来说 使用\w 贪婪匹配字符串这里就分割成了112233168com三个字符串 再加上一个成为\w限定字符串是以结尾的 接着就是域名了比如QQ邮箱就是qq.com还有168.com等等一般是数字和小写字母[0-9a-z] 再跟着一个就是贪婪匹配数组和小写字母的字符串的最大长度 最后是那个com用小写字母匹配再贪婪一下就好了 匹配网址 (https:|http:\ftp:)//\S 首先是常见的网址开头有三个https和http以及ftp 因为这三个头都有可能所以想要全部匹配需要用到圆括号括起来再用一个竖“|”表示或就可以了 去除开头剩下的内容不为空就可以了用\S贪婪一下 匹配身份证号 \d{17}[xX\d]
一般是17位数字加上最后一位可能是x也可能是数字
正则表达式re模块
一个负责处理字符串内容的模块也就是网站中超多的源代码超文本等等
这也是python内置的模块不需要下载直接导入就可以了
因为要在正则表达式中频繁使用转义字符在python解释器中更期望两个反斜杠的用法
比如在字符串中使用反斜杠要注意转义的问题这里使用\d{4}会报错使用\\d{4}就没事了
最低级的字符串处理函数match
这个函数只能完全匹配字符串才能提取内容
这个函数的返回值不能直接使用需要配合group函数才能把match识别的信息提取出来
把需要提取的信息用正则表达式括起来group(1)对应的就是第一个括号提取的内容group(2)对应的就是第二个括号了剩下的一次类推
还有一种方法可以一次性提取所有括号的内容也就是groups()这个函数会把提取的内容放到一个元组里面可以以数组的形式打印出来
text 姓名小明 出生日期2000年1月1日 毕业年龄2024年6月1日# match 函数只能从起始位置匹配整个字符串最麻烦的一种一旦“姓名”改成“1姓名”就识别不出来了t1 re.match(姓名,text)
print(t1)
# 如果直接打印match的返回值就会显示所有内容如re.Match object; span(0, 3), match姓名
# 如果这个字符串里面没找到macth第一个参数的字符串就会返回“NoneType”
# 这里还要进一步筛选使用group函数返回match识别的内容
print(t1.group()) # 姓名# 这里可以用正则表达式对字符串进行划分
# 如
t2 re.match(姓名.*出生日期\\d{4},text)
print(t2.group()) # 姓名小明 出生日期2000t2 re.match(姓名.*出生日期\\d{4}.*毕业年龄\\d{4},text)
print(t2.group()) # 姓名小明 出生日期2000年1月1日 毕业年龄2024t2 re.match(姓名.*出生日期(\\d{4}).*毕业年龄(\\d{4}).*,text)
print(t2.group(1)) # 给想要提取的信息加上圆括号再使用group函数从左往右从1开始打印匹配的内容
# t2.group(1) 就是2000
# t2.group(2) 就是2024
print(t2.groups()) # 也可以把这些内容以元组的形式打印出来 (2000, 2024)
print(t2.groups()[0]) # 这里就是打印元组的第0个元素 2000中级字符串处理函数search
这个函数比较高级不需要完全匹配字符串它可以自己去找对应的位置如果找不到则返回NoneType
这些函数找不到对应内容基本上都是返回这个语句NoneType
这个函数只能返回第一个匹配的内容意思就是处理多个相同内容时这个语句只能返回一次内容
text2 姓名小明 出生日期2000年1月1日 毕业年龄2024年6月1日
t1 re.search(出生日期(\\d{4}).*年龄(\\d{4}),text2)
print(t1.group(1),t1.group(2),t1.groups())# search中使用groups也是放到元组里面
# 扫描整个字符串返回第一个与之匹配的内容意思就是处理多个相同内容时这个语句只能返回一次内容高级字符串处理函数findall 也是常用的函数
这个函数可以一次性扫描整个字符串并把匹配的内容打印出来如果有多个匹配的内容就以列表的形式打印出来
# re.findall 直接打印会放到一个列表里面想要提取出来只需要和使用数组一样加个方括号就好了
text3 姓名小明 出生日期2000年1月1日 毕业年龄2024年6月1日
t3 re.findall(出生日期(\\d{4}).*年龄(\\d{4}),text3) # 如果要识别的内容有多个就会先放到列表里面再放到元组里面想要提取内容需要再使用方括号
print(t3) [(2000, 2024)]text4
姓名小明 出生日期2000年1月1日 毕业年龄2024年6月1日
姓名小李 出生日期2001年2月1日 毕业年龄2024年7月1日
姓名小张 出生日期2002年2月2日 毕业年龄2024年8月1日
t4 re.findall(出生日期(\\d{4}).*年龄(\\d{4}),text4)
print(t4) # 对于多个相同内容的字符串就会把一行里面的内容放到一个元组里面去有几组内容就有几个元组 [(2000, 2024), (2001, 2024), (2002, 2024)]t5 re.findall(出生日期(.*?) 毕业,text4) # 使用非贪婪模式避免匹配的内容不理想
print(t5) [2000年1月1日, 2001年2月1日, 2002年2月2日]当然现在的编译器比较智能了这个贪婪与非贪婪有的时候没啥区别为了严谨一些还是自己区分一下吧
字符串替换函数sub
这个的使用原理和replace函数是一样的但是sub可以使用正则表达式在处理问题的时候更加灵活
第一个参数是正则表达式第二个是要替换的内容第三个是参考文本
不会改变原来的内容只是在原来的基础上更改后保存在新的变量中
# re.sub() 替换函数
text5 0566-12345678
t6 re.sub(^\\d{4},0333,text5) # 第一个参数是正则表达式第二个是要替换的内容第三个是参考文本
print(t6,text5) # 并不会对原来的文本进行替换只是把替换后的内容放到新变量里面text6 0566-123123123.replace(0566,0333)
print(text6) # 他们的效果一样但是sub更灵活当内容不一样时还是正则表达式通用案例 小说筛选器
text
div classnoveContent
p1945年四月山城。/p
p宋孝安果党军统内勤处第三科科长中校军衔因为信佛又极为擅长布局、算无遗策被称之为‘军统小诸葛’。/p
p此时他神情紧张听着戴老板办公室时不时传出来的呵斥声让他额头上的汗珠没断过。/p
p手上拿着佛珠不停地转动着“明诚长官你可把我害惨了啊。”/p
p此时他不祈祷能够升官发财只求不被枪毙。/p
p戴笠军事委员会调查统计局副局长军衔少将实际上是军统的实际控制人此时他非常生气看着眼前的陈汉文一手叉腰一手拿着手绢指着对方“谁让你跑到沪上杀人的76号总部被你炸上了天李士群死了丁默邨被你刺成重伤如果不是他运气好现在也被你送上天了你想怎么样我有没有和你说过我们和76号不是敌人在陕北的共党才是还有新四军……”/p
p陈汉文坐在沙发上旁边戴笠的训斥就当没听到看到水烧开后他端起来开始泡茶。/p
p陈汉文字明诚少将衔军统总务处处长。/pt7 re.sub(.*?, ,text)
print(t7) # 完成了字符串的替换这里是把网页的格式字符全部替换为空字符串分割函数split
第一个参数是想要分割的标识符像是逗号句号或者空格
第二个参数是需要处理的文本
# re.split() 字符串分割 按照设计的正则表达式的格式把字符串分割后放在列表里面
text4 你好 python 工程师
t9 re.split( ,text4)
print(t9) # [你好, python, 工程师]text4 你好,python.工程师
t9 re.split([,.],text4) # 使用正则表达式更灵活 上面的那个也可以是t9 re.split([ ],text4)
print(t9) # [你好, python, 工程师]换行加注释
注意在多行语句组合一句话的时候需要用三个”“”双引号“”“
# 换行加注释
text2 姓名小明 出生日期2000年1月1日 毕业年龄2024年6月1日
t10 re.findall(
出生日期(\\d{4}) # 现在就可以添加注释了
.*年龄(\\d{4}) # 比如解释说明这里提取的是毕业年龄
,text2,re.VERBOSE) # re.VERBOSE 可以忽略#和换行符以及中间的内容
print(t10)findall的第三个参数re.VERBOSE意思是告诉程序在正则表达式里面换行了而且可能还有注释
re.VERBOSE 标志通常与 re 模块中的 re.compile()、re.search()、re.match()、re.finditer() 等函数一起使用。这样可以很方便地写出复杂的正则表达式同时在其中添加注释和换行使其更加清晰。
正则表达式预处理compile
正则表达式的预处理因为对于字符串的处理可能高达上万次那么我们就需要考虑一下预处理
把正则表达式提前处理成被执行的格式如果不预处理每次遇到字符串都要重新编译预处理提高效率
# 预编译re.compile
text5 姓名小明 出生日期2000年1月1日 毕业年龄2024年6月1日
tt re.compile(出生日期(\\d{4}).*年龄(\\d{4})) # 正则表达式预处理格式放在tt里面
t11 re.findall(tt,text5) # 按照tt的格式对text5处理
print(t11)正则表达式的转义字符与原生字符
对于一个语句中如果含有转义字符比如“\d”,“\n”等等
我们想要直接打印就会出现问题
text hello\nihao\didi
print(text)如果你是在python解释器pycharm中复制这段话就会发现\n跟其他的字符不太一样
你尝试打印一下
hello ihao\didi
就是这种结果可能程序还会报错因为\d是字符串插入数字的标识符
\n 没了
因为\n有自己的意思是换行符也是在字符串中出现
现在你不想转义就想打印hello\nihao\didi这句话 要么在已有的反斜杠前面或者后面再加一个反斜杠组成两个反斜杠 要么就是在字符串前面加一个小写的r比如text r’hello\nihao\didi’ 这个小写的r原型是raw意思是原生就是告诉程序别转义
案例 美元$与正则表达式取结尾$
This apple is available for $5 and $7
如果直接放在正则表达式里面想要提取5美元和7美元这个价格
很明显我们需要的是$这个标识符还有\d
解决方式有两个
”\$\d“r$\d
案例 以反斜杠作为标识符
abc123\water
在这里我们想要提取反斜杠前面的123这个数值
需要用到反斜杠作为标识符我们想要的是”(\d)\“
可惜的是你使用字符串函数并把这个内容作为正则表达式会报错
第二个反斜杠会和后面的引号结合此时你想再用一个反斜杠很抱歉又错了在程序预编译的时候会处理掉一个反斜杠
程序又会把这个反斜杠和引号结合
所以只能再加两个反斜杠也就是4个反斜杠在预编译的时候每两个反斜杠消去一个最后真正执行的时候还剩两个程序就能识别了
text hello\water
t1 re.findall((\d)\\\\,text)
print(text)上面这些话证明了使用反斜杠来转转义字符很麻烦我们只需要
t1 re.findall(r(\d)\\,text)使用r固定这个字符串不让程序在预处理的时候消去反斜杠就可以了
案例 匹配网页的标题
首先拿到两个源代码分析一下发现标题从后面开始A站前面结束
我们直接放到正则表达式里面然后把想要提取的部分弄成.*?
text1 meta namekeywords content【AC独家】千星绪~又谁在月牙下做一曲新谣,舞蹈·偶像,宅舞,楚殅南w,A站,AcFun,ACG,弹幕
text2 meta namekeywords content戴眼镜的可以吗......(今日开心视频1541),娱乐,搞笑,表情笑笑坊,A站,AcFun,ACG,弹幕
t1 re.findall(meta namekeywords content(.*?),A站,AcFun,ACG,弹幕,text1)
t2 re.findall(meta namekeywords content(.*?),A站,AcFun,ACG,弹幕,text2)
print(t1,t2)这个案例是想要讲带有表情的网址比如B站的 哔哩哔哩 (゜-゜)つロ 干杯~-bilibili这个表情
有时候也会在正则表达式里面出现如果正则表达式里面有这些括号什么的也需要反转义字符
正则表达式xPath模块XML Path Language
这个模块主要是处理网页(XML和HTML)源代码里面的内容因为大部分的信息都是放在源代码的元素和属性中
像是字符串、图片、音频等等
xPath节点
在源代码中有七种类型的节点
元素属性文本命名空间处理指令注释文档根节点
XML文档被xPath看成节点树也是从根出发用Tab对齐来控制子节点父节点这些节点之间的关系
xPath语法
表达式描述nodename直接写这个节点的名字就是选它所有的子节点/一个斜杠从根节点选取//两个斜杠就是从整个源代码中任意位置匹配节点相对简单一些不用找头节点.一个点意味着选取当前节点…两个点选取当前节点的父节点选取属性
路径表达式结果bookstore选取 bookstore 元素的所有子节点。/bookstore选取根元素 bookstore。注释:假如路径起始于正斜杠(/)则此路径始终代表到某元素的绝对路径!bookstore/book选取属于 bookstore 的子元素的所有 book 元素//book选取所有 book子元素而不管它们在文档中的位置bookstore//book选择属于 bookstore 元素的后代的所有 book 元素,而不管它们位于 bookstore 之下的什么位置。//lang选取名为lang 的所有属性。
谓语
谓语用来查找某个特定的节点或者包含某个指定的值的节点。谓语被嵌在方括号中。
路径表达式结果/bookstore/book[1]选取属于 bookstore 子元素的第一个 book 元素。/bookstore/book[last()]选取属于 bookstore 子元素的最后一个 book 元素。/bookstore/book[last()-1]选取属于 bookstore 子元素的倒数第二个 book 元素。/bookstore/book[position()❤️]选取最前面的两个属于bookstore 元素的子元素的book元素//title[lang]选取所有拥有名为lang 的属性的 title 元素。//title[lang‘eng’]选取所有 title 元素且这些元素拥有值为eng的lang 属性/bookstore/book[price35.00]选取 bookstore元素的所有book元素且其中的price 元素的值须大于35.00。/bookstore/book[price35.00]/title选取bookstore元素中的book元素的所有title 元素且其中的 price 元素的值须大于 35.00。
选取未知节点
通配符描述/bookstore/*选取bookstore元素的所有子元素*选取文档所有元素//title[*]选取所有带有属性title的元素
选取若干路径
使用“|”可以一次访问多个对象
路径表达式结果//book/title|//book/price选取book元素的所有title和price元素//title|//price选取文档中所有title和price元素/bookstore/book/title|//price选取属于bookstore元素的book元素的所有title元素以及整个文档中所有的price元素
数据解析
xPath-lxml库的使用
lxml库
Ixml 是 一个HIML/XML的解析器主要的功能是解析和提取HTML/XML 数据Ixml和正则一样也是用C语言实现的是一款高性能的 Python HIML/XMI解析器我们可以利用之前学习的XPath语法来快速的定位特定元素以及节点信息。lxml python 官方文档:http://lxml.de/index.html
lxml库-读取本地xml文件
我们可以利用他来解析IIML代码并且在解析HIML代码的时候如果HIML代码不规范他会自动的进行补全。from lxml import etree 这是导入模块 xml etree.parse(xPath test1.html’) 这里是把本地文件读入xml或者html直接打印是一颗etree树parse对文档的语法极为严格少一点就会报错 print(etree.tostring(xml).decode(unicode )) etree.tostring(xml) 这里是把树拿出来了但是以字节byte的形式打开的所以还要etree.tostring(xml).decode( )再加一个函数意思是以字符串的形式打开
使用HTML的方式读入文件
使用HTML读文件的时候对文件比较友好可以抛弃parse了
定位元素 定位“xPath练习” /head/title/text() 意思是找到节点head再找title节点找到title节点之后再访问title节点里面的文本信息 定位”百里守约“ /body/div/p/text() 定位”李清照“ /body//div[class“song”]//p/text() 定位body节点找属性值位class“song”的div节点再访问下面的所有p节点打印p节点的文本部分 有多个对象的时候会得到一个列表这里的李清照排在第一位可以使用列表的形式得到信息 假如我们用对象result来接收这个列表打印李清照就是result[0] 如果是倒数第3个也可以是result[-3] 定位属性”song“ 这里是获取节点的属性值 有时候一些信息是包在属性里面我们就需要得到他们 //body//div/class 这个操作就是先找body节点下面的div节点的属性class的值也就是等号后面引号包起来的东西
按标签顺序定位元素
这里的李清照是第一个p节点
可以使用//body//div[class“song”]/p[1]/text() (从1开始)
last元素指最后一个标签比如上面的最后一个是苏轼那么p[last]就是苏轼
按照包含的元素定位
上面的图片中在body节点下面有两个div标签这里的第二个div标签还包含一个a节点
所以每个div标签还能根据包含的子节点来定位定位第一个div//div[p]
定位第二个div节点//div[p and a] 意思就是这个div下面有两个子节点a和p
/与//的区别
单个/意思就是从当前位置查找就是你需要先找到一个确定的节点才能使用/
单/不太好用需要写的特别具体就像文件操作里面的绝对路径一样需要从盘开始定位
双//就是从任意位置查找这里我们一般给了属性值就只能找到一个节点也就是我们想要的东西这里就是文件操作的相对路径写的东西比较少
python案例—下载人生格言
python项目实战-xPath下载人生格言-CSDN博客
python案例—下载美女图片
python项目实战——下载美女图片-CSDN博客
案例总结
写完这两个案例之后感觉使用xPath语法提取内容特别简单
就是确定内容都在源代码里面把这个源代码转换成etree树以节点树的方式去获取信息
然后就是固定的几步
使用requests模块得到源代码使用etree.HTML函数把源代码转换成节点树使用xPath语法提取内容
掌握了这个语法之后感觉一些比较基础的网址的任何内容都可以爬取了
比如我为了查看自己在CSDN上发表的博客的浏览量写了python实现csdn文章浏览量日志-CSDN博客
觉得只是查看没意思就加了一个获取时间的库写成日志记录我每次查看的时间和得到的数据
这里还能改一改比如把查看的博客名称也记录在本地文件中格式就是“博客名称浏览量记录时间”
回头看看之前那个豆瓣电影也能爬取了暂且先不做了继续往下开搞
BS4–beautifulsoup4库
和lxml库一样都是对HTML/XML的解释器主要功能也是提取数据
lxml是局部遍历beautiful soup是基于HTMl DOM(document object model)也就是整个文件解析DOM树
因为是对全部内容解析所以性能要更低一点
BS4语法
导入库
from bs4 import BeautifulSoup
实例化
soup BeautifulSoup(html,‘html.parser’) 第一个参数是html的文档第二个是解析方式
解析方式有两种html.parser和lxml区别就是对字符的补全方式不同
一般使用第一个解析方式
soup库自带一个整理格式的函数soup.prettify()这个函数就跟之前的pprint一样但是这个是以html的格式显示
对象的种类
Tag: Tag 通俗点讲就是 HTML 中的一个个标签。我们可以利用 soup 加标签名轻松地获取这些标签的内容这些对象的类型是bs4.element.tag。但是注意它查找的是在所有内容中的第一个符合要求的标签。NavigableString 如果拿到标签后还想获取标签中的内容。那么可以通过tag.string获取标签中的文字。BeautifulSoup: BeautifulSoup 对象表示的是一个文档的全部内容,大部分时候,可以把它当作Tag 对象,它支持 遍历文档树 和 搜索文档树 中描述的大部分的方法.Comment: Tag,NavigableString,BeautifulSoup 几乎覆盖了html和xml中的所有内容,但是还有一些特殊对象,容易让人担心的内容是文档的注释部分Comment 对象是一个特殊类型的NavigableString 对象
这个Conmment就是针对注释部分的类型
获取标签
print(soup.a) 就可以拿到整个a标签的全部内容包括属性和文本部分
soup.a的类型就是bs4.element.Tag
获取标签的名称
print(soup.a.name) 打印出来就是a
获取标签的属性/属性值
print(soup.a.attrs) 打印出来的就是一个字典包含是所有的属性{’属性名‘’属性值‘’属性名‘’属性值‘’属性名‘’属性值‘}
想要打印具体的属性值
print(soup.a[‘href’]) 打印的就是a标签的href属性的值
还有另一种方式print(soup.a.get(‘href’)) 和上面那个一样
改变标签的属性值
soup.a[‘href’] ‘hello’
print(soup.a)
获取标签的文本内容
print(soup.a.string) 就相当于lxml里面的//a/text()
print(soup.a.text)
print(soup.a.get_text())
这三个都是一样的效果他们的type都是bs4.element.NavigableString
获取注释部分的内容
b
!–Hey,How are you?–
b
上面这个以感叹号开头的标签是一个注释
# 导入库
from bs4.element import Comment
print(soup.b.string) # Hey,How are you?
print(ytype(soup.b.string)) # 类型是bs4.element.Comment遍历文档树
contents和children : contents:返回所有子节点的列表 就是以列表的形式打印节点 children:返回所有子节点的迭代器 打印子节点默认不显示换行符可以使用repr的方式显示出来strings 和 stripped_.strings strings:如果tag中包含多个字符串 ,可以使用 .strings 来循环获取 stripped_strings:输出的字符串中可能包含了很多空格或空行,使用 .stripped_strings 可以去除多余空白内容
body_tag soup.body
print(body_tag.contents) 打印列表
for tag in body_tag.children: 循环得到子节点print(tag) 打印子节点print(repr(tag)) 显示换行符的方式打印子节点 与上一条语句有一个就行for tag in body_tag.strings:print(tag)
# 直接打印不显示换行符有很多空白
# 使用repr函数显示换行符减少空白
# 但是不想要换行符也不想要空白
for tag in body_tag.stripped_.strings:print(tag)# 可以去除换行符也没有空行搜索文档树
find和find_all方法: 搜索文档树一般用得比较多的就是两个方法一个是find一个是find_all。 find方法是找到第一个满足条件的标签后就立即返回只返回一个元素。 find_all方法是把所有满足条件的标签都选到然后返回。
find
tr soup.find(tr)
print(tr)
# 会把第一个tr标签包含的所有内容全部打印出来find_all trs soup.findall(tr)
print(trs)
# 返回的是一个列表包含的是所有的tr标签
# 使用for循环可以打印出来
for tr in trs:print(tr)print(--*30) # 分隔符获取idnr’的标签有两种写法
直接写属性
trs soup.find_all(tr,idnr)
for tr in trs:print(tr)print(***30)在获取tr标签的基础上多了一个条件idnr
以字典的形式把属性以键值对的形式填入
trs soup.find_all(tr,attrs{id:nr})
for tr in trs:print(tr)print(***30)他们的效果是一样的
获取有两个特定属性的标签
tds soup.find_all(td,class_odd,aligncenter) # 因为class是python的一个关键字这里不想作为关键字使用需要额外在后面加一个下划线
for td in tds:print(td) # 直接打印td得到的是所有的td标签
for td in tds:print(td.text) # 加个后缀就可以得到文本部分了 # 根上面一样也有两种写法
tds soup.find_all(td,attrs{classodd,align:center}) # 跟上面的效果是一样的获取所有属性值target‘_blank的a标签的href的属性
a_list soup.find_all(a,target‘_blank)
for a in a_list:print(a) # 直接打印得到的是a的整个标签页
for a in a_list:print(a[href]) # 这里是只打印a标签的href的值
for a in a_list:print(a.get(href)) # 这里跟上面的效果一样获取多个内容这里是从小说网页上爬取小说的作者名称链接日期等信息以列表的形式打印
trs soup.find_all(tr,id nr) # 这里是获取了一本小说的整个标签
info [] # 创建空列表放小说的基本信息
# 下面就是从获取的整个标签里面摘除想要的信息
for tr in trs:tdstr.find_all(td) # 这里是进一步取出标签信息link tds[0],a[href] # 获取a标签的href属性值name tds[0].a.string # 获取a的文本部分author tds[2].text #获取第3个标签的文本部分date tds[4].text # 获取第5个标签的文本部分info.append([link,name,author,date]) # 把信息存入列表print(info)上面的代码展示了获取信息的多种方式比如stringgettext等等
CSS选择器
使用该选择器需要使用soup.select方法 查找所有的tr标签 print(soup.select(‘tr’)) 查找类名是odd的标签这里的类就是class也就是class‘odd’ print(soup.select(‘.odd’)) 意思就是class’odd‘但是可以简写成’.‘odd’ 查找所有id是center的标签 print(soup.select(‘#center’)) 意思就是id‘center’ 这里也是简写’#‘center’ 组合查找 p标签下面包含属性值为idlink2的标签 print(soup.select(‘p #link2’)) 先写一个p说明想要的是p标签后空一格说明要找下面的值即idlink2 如果不加空格说明这个属性值是p的就会按照既是p标签还有属性值的p就不再是p标签下面的某标签的属性值 p标签下面的所有a标签 print(soup.select(‘p a’)) 查找属性值 target _blank的 a 标签 print(soup.select(‘a[target“_blank”]’)) 获取属性值及文本内容 获取a标签的所有文本内容及href的属性值 a soup.select(a)
for i in a:print(i.text)print(i.get(href))这些文本操作都差不多都是筛选信息的