网页内容提 取
使用不同技术提取、定位网页内容
使用正则、xpath获取网页内容
正则
以如下html代码为例:
2024必看热片
<ul>
<li>
<a href="/i/113004.html">2024年国产6.4分犯罪片《浴火之路》</a>
</li>
<li>
<a href="/i/112875.html">2024年国产犯罪片《逆鳞》</a>
</li>
</ul>
1.生成正则表达式对象
reMainUrl = re.compile(r"2024必看热片.*?<ul>(?P<ul_str>.*?)</ul>", re.S)
-
re.compile(r"2024必看热片.*?<ul>(?P<ul_str>.*?)</ul>", re.S)
:- 这段代码用正则表达式的模式字符串创建了一个正则表达式对象。这个对象可以被用来进行匹配操作(如
search
、match
、findall
等)。 re.S
是一个标志,它告诉正则表达式引擎,匹配时.
(点号)可以匹配换行符。
- 这段代码用正则表达式的模式字符串创建了一个正则表达式对象。这个对象可以被用来进行匹配操作(如
-
正则表达式
r"2024必看热片.*?<ul>(?P<ul_str>.*?)</ul>"
的含义:r""
表示这是一个原始字符串(raw string),在 Python 中原始字符串会忽略反斜杠的转义作用,这对于正则表达式很常见。"2024必看热片"
:表示匹配字符串2024必看热片
。.*?
:表示匹配任意字符(包括换行符,因为re.S
标志使得.
可以匹配换行符),并且是 非贪婪匹配,即尽可能少地匹配字符。<ul>(?P<ul_str>.*?)</ul>
:<ul>
和</ul>
:匹配<ul>
标签和</ul>
标签。(?P<ul_str>.*?)
:这是一个命名捕获组,表示匹配<ul>
标签内的内容。?P<ul_str>
是命名捕获组的语法,表示将匹配的内容保存在名为ul_str
的组中。.*?
是非贪婪匹配,它会尽量少地匹配字符,以确保不会匹配到多个<ul>
标签之间的内容。
-
re.S
标志:- 使得
.
可以匹配换行符(\n
)。因此,正则表达式中的.*?
可以跨越多行匹配内容。
- 使得
2.通过迭代器输出匹配结果
# resp.text为上方声明的html代码
iterUrl = reMainUrl.finditer(resp.text)
for u in iterUrl:
ul = u.group('ul_str')
iterUrl
中存在一个匹配项,匹配项的内容被存储在名为ul_str
的组中,通过u.group('ul_str')
可获取到其内容:
<li>
<a href="/i/113004.html">2024年国产6.4分犯罪片《浴火之路》</a>
</li>
<li>
<a href="/i/112875.html">2024年国产犯罪片《逆鳞》</a>
</li>
同样的,我们可以对上述结果继续进行同样的处理:
for u in iterUrl:
ul = u.group('ul_str')
reSubUrl = re.compile(r"<a href='(?P<url>.*?)'", re.S)
# 获取a标签内的href地址,存入名称为url的组内
iterUrl = reSubUrl.finditer(ul)
for url in iterUrl:
# 遍历迭代器,通过 迭代项.group(组名) 可获得实际存储的内容
print(url.group('url'))
对迭代器每一项进行group
方法取值打印:
/i/113004.html
/i/112875.html
3.代码示例
获取网站片单,对影片简介进行分类信息提取。
思路 1.访问主页,定位到“2024必看热片”,获取对应的详情链接列表 2.遍历详情链接,定位并获取影片详情信息 3.将影片信息分组存储
import re, requests
domain = "https://dytt89.com"
pageEncoding = "gb2312"
req_param = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
}
# 执行请求(传入请求头,关闭校验)
resp = requests.get(domain, headers=req_param, verify=False)
# 使用特定编码解析响应
resp.encoding = pageEncoding
# print(resp.text)
# 创建正则表达式对象,逐步筛选所需的内容
reMainUrl = re.compile(r"2024必看热片.*?<ul>(?P<ul>.*?)</ul>", re.S)
reSubUrl = re.compile(r"<a href='(?P<url>.*?)'", re.S)
# 基于特定正则表达式,对特定文本进行搜索
iterUrl = reMainUrl.finditer(resp.text)
# 遍历片单,获取所有影片的详情URL
childUrlList = []
for u in iterUrl:
ul = u.group('ul')
iterUrl = reSubUrl.finditer(ul)
for url in iterUrl:
childUrlList.append(domain + url.group('url'))
# 打开文件
f = open("dytt89.csv", mode="w", encoding="utf-8")
# 创建csv写入器对象
csv_writer = csv.writer(f)
i = 0
# 遍历URL
for url in childUrlList:
# 测试用,仅抓取5条影片的详情信息
if i > 5:
break
# 获取影片详情叶敏
childResp = requests.get(url, headers=req_param, verify=False)
childResp.encoding = pageEncoding
reName = re.compile(r"◎片 名(?P<name>.*?)<br />.*?"
r"◎类 别(?P<category>.*?)<br />.*?"
r"◎上映日期(?P<showTime>.*?)<br />.*?"
r"◎豆瓣评分(?P<douban>.*?)<br />.*?"
r"◎导 演(?P<director>.*?)<br />.*?"
, re.S)
# search方法用于获得第一个匹配项(该页面中仅有一处影片描述)
result = reName.search(childResp.text)
# 将result转化为字典,key为组名,value为组值
dic = result.groupdict()
# 向csv文件写入所有的组值
csv_writer.writerow(dic.values())
i += 1
# 关闭响应连接
resp.close()
xpath
1.获取html对象
# 将响应结果提取为HTML格式
popularHtml = etree.HTML(popularResp.text)
2.提取路径数据
对于如下html:
<equip>
<id>xx1</id>
<name>消防箱</name>
<sub>
<equip>
<id>xx1.1</id>
<name>水枪</name>
</equip>
<equip>
<id>xx1.2</id>
<name>防毒面罩</name>
</equip>
<equip>
<id>xx1.3</id>
<name>撬棍</name>
</equip>
</sub>
<plan>
<name class="planNameClass">巡检计划2024</name>
<div id="abc">
<a href="/detail/abc">点击跳转到该计划详情</a>
<a href="/overview/abc">点击跳转到该计划总览</a>
</div>
</plan>
</equip>
使用xpath
方法提取数据:
result = tree.xpath("/equip/name/text()")
result = tree.xpath("/equip/sub/equip/name/text()") # 所有相同元素下的文本
result = tree.xpath("/equip//name/text()") # 遍历,所有相同元素下的文本
# 查找特定属性的元素下的文本
result = tree.xpath("/equip/plan/name[@class='planNameClass']/text()")
# 查 找特定属性的元素下的属性值(@属性名称)
result = tree.xpath("/equip/plan/div/a/@href")
# 查找特定属性的元素下的href,使用序号
result = tree.xpath("/equip/plan/div/a[2]/@href")
3.代码示例
获取网站供应商列表。
import requests
from lxml import etree
domain = "https://www.zbj.com/dp/"
response = requests.get(domain, headers={'User-Agent': 'Mozilla/5.0'})
response.encoding = 'utf-8'
html = etree.HTML(response.text)
result = html.xpath("/html//div[@class='search-result-list-shop-wrap']//a//div[@class='demand-headline alter']/text()")
print(result)
response.close()
json
1.获取json对象
通过调用Response
对象的json()
方法可以将json响应解析为python对象:
# 将解析结果存入变量
dic = detailResp.json()
获取到的json:
{
"resultCode":"1",
"resultMsg":"success",
"reqId":"bf843b65-efc5-4941-a9f2-6756ed431968",
"systemTime": "1734581401990",
"videoInfo": {
"playSta":"1",
"video_image":"https://xxx.png",
"videos":{
"hdUrl":"",
"hdflvUrl":"",
"sdUrl":"",
"sdflvUrl":"",
"srcUrl":"https://xxxx"
}
}
}
2.提取数据
使用键值对的方式提取值:
src = dic['videoInfo']['videos']['srcUrl']
time = dic['systemTime']
3.代码示例
获取网站视频排行榜,下载每个视频。
思路 1.访问排行榜, 获取到视频详情地址列表 2.遍历视频地址列表,访问视频详情页,获取视频源参数 3.处理视频源参数,获取视频真实的存储地址,下载视频
import csv
import re
import lxml
import requests
from lxml import etree
# 网站主页
domain = "https://www.pearvideo.com"
# 视频详情页
videoDomain = "https://www.pearvideo.com/videoStatus.jsp"
# 视频排行榜页面
popularDomain = "https://www.pearvideo.com/popular"
# 获取排行榜页面代码
popularResp = requests.get(popularDomain)
popularResp.encoding = 'utf-8'
popularHtml = etree.HTML(popularResp.text)
popularResp.close()
# 筛选视频地址、视频标题
marks = popularHtml.xpath("/html//ul[@class='popular-list']//a/@href")
titles = popularHtml.xpath("/html//ul[@class='popular-list']//a/h2/text()")
marksList = []
titlesList = []
for mark in marks:
marksList.append(mark)
for title in titles:
titlesList.append(title)
# 遍历视频地址
for i in range(min(len(marksList), len(titlesList))):
# 验证提取的视频信息
print(titlesList[i] + "," + marksList[i])
# 提取内容id
contId = marksList[i].split("_")[-1]
# 请求影片详情页面,获取视频地址信息
detailResp = requests.get(videoDomain,
headers={
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
"Cookie": "PEAR_UUID=0daa7a77-909a-42ac-8415-ccbdfbe601ab; _uab_collina=173389892555354962793486; Hm_lvt_9707bc8d5f6bba210e7218b8496f076a=1733898926; HMACCOUNT=FE1AD1F5E2F03B8A; p_h5_u=0418AB60-7B36-4922-835C-2A94866C9933; Hm_lpvt_9707bc8d5f6bba210e7218b8496f076a=1733901580; tgw_l7_route=c94993c99ec065901a2ef1434ac92da5; JSESSIONID=806C088B0DA88008E1D672235DF0E54A",
"Referer": domain + "/" + marksList[i]
},
params={
"contId": contId,
})
detailResp.encoding = 'utf-8'
# 提取视频地址关键信息:src time
# 方式1: 正则
# re_src = re.compile(r'.*?"srcUrl":"(?P<srcUrl>.*?)".*?')
# re_time = re.compile(r'.*?"systemTime": "(?P<time>\d+)".*?') # src = re_src.search(detailResp.text).group("srcUrl") # time = re_time.search(detailResp.text).group("time")
# 方式2: json dic = detailResp.json()
src = dic['videoInfo']['videos']['srcUrl']
time = dic['systemTime']
# 替换关 键字,生成真实的视频下载地址
real_video_url = src.replace(time, f"cont-{contId}")
print(real_video_url)
# 下载视频
with open(f"{contId}.mp4", "wb") as f:
f.write(requests.get(real_video_url).content)
print(f"video download complete: {titlesList[i]}")
# 测试用,仅下载一条视频
break
BeautifulSoup
1.解析页面
page = BeautifulSoup(resp.text, 'html.parser')
2.获取目标节点
# 获取第一个匹配的节点: id=myposts的div标签
div = page.find("div", attrs={"id": "myposts"})
# 获取所有匹配的节点: class=PostList的div标签
postLists = div.find_all("div", attrs={"class": "PostList"})
3.代码示例
下载图片网站主页加载的图片。
import os
import re
import cloudscraper
import requests
from bs4 import BeautifulSoup
srcList = []
# 获取url
def geturl():
domain = "https://www.bizhihui.com/fengjing/"
resp = requests.get(domain, headers={
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"
})
resp.encoding = "utf-8"
# print(resp.text)
soup = BeautifulSoup(resp.text, "html.parser")
resp.close()
divs = soup.find_all("ul", attrs={"id": "item-lists"})
for lis in divs:
li_list = lis.find_all("li",
attrs={"class": "item-list masonry-brick"})
for li in li_list:
# 获取图片src
search = re.compile(r"(?P<src>.*?)-pcthumbs")
.search(li.img["src"]).group("src")
srcList.append(search)
break
# 存储url
def store():
with open("picUrl.csv", mode="w", newline="", encoding="utf-8") as f:
for e in srcList:
f.writelines(e + "\n")
print(f"query {len(srcList)} pic's url")
return 1
# 主 程序
with open("picUrl.csv", mode="r", encoding="utf-8") as f:
# csv中不存在信息时执行抓取
if len(f.read()) == 0:
print("start crawling pic url...")
geturl()
store()
# 读取csv,批量下载
print("start download pic")
for src in f.readlines():
src = src.strip()
# 排除空行
if len(src) == 0:
continue
img_name = "./pics/" + src.split("/")[-1]
# 图片已存在时跳过
if os.path.exists(img_name):
continue
with open(img_name, "wb") as imgF:
imgF.write(requests.get(src).content)
print(f"download complete: {img_name}")
print("download pic over!")