bs4解析(python独有)
数据解析的原理:标签定位;提取标签、标签属性中存储的数据
bs4数据解析的原理:
- 实例化一个BeautifulSoup对象,并且将页面源码数据加载到该对象中
- 通过调用BeautifulSoup对象中相关的属性或者方法进行标签定位和数据提取
环境安装:pip install bs4、pip install lxml
实例化BeautifulSoup对象:
- from bs4 import BeautifulSoup
- 对象的实例化:将本地的html文档中的数据加载到该对象中;将互联网上获取的页面源码加载到该对象中
from bs4 import BeautifulSoup
# 将本地的html文档中的数据加载到BeautifulSoup对象中
layout = open("./layout.html", 'r', encoding='utf-8')
soup = BeautifulSoup(layout, 'lxml')
print(soup)
获取标签
soup.div # 返回第一次出现的标签对应的内容
soup.tagName:返回的是文档中第一次出现的tagName对应的标签
soup.find('div') # 返回第一次出现的标签对应的内容
soup.find()等同于soup.tagName
soup.find('div',class_='card') # class="card"对应的标签的内容
soup.find(‘div’,class_/id/attr=‘card’):属性定位
soup.find_all('a') # 返回符合要求的全部标签对应的内容(列表形式)
soup.find_all(‘tagName’):返回符合要求的所有标签
soup.select('.card') # 返回class='card'对应的标签
soup.select(‘id选择器/class选择器/标签选择器/层次选择器’)
获取标签之间的文本数据
soup.tagName.text/string/get_text()
soup.find('p').text
- text/get_text():可以获取标签中的所有文本内容(即使该文本内容不是该标签的直系文本内容)
- string:只可以获取该标签下面直系文本的内容
获取标签中属性值
soup.a['href']
补充:css层次选择器
后代选择器
/*后代选择器*/
body p{
/*就是对body后面所有的标签内容进行属性的调节*/
background: red;
}
后代选择器:爷爷 父亲 儿子
子类选择器
body >p{
background: blue;
/*就是一代的关系*/
}
相邻兄弟选择器
同辈的关系(姐姐,妹妹)
/*相邻兄弟选择器*/
.active +p{
/*向下,只有一个*/
background: brown;
}
通用选择器
当前选中元素向下的所有兄弟元素
.active ~p {
background: blanchedalmond;
}
实例:爬取三国演义中的内容
url:https://www.shicimingju.com/book/sanguoyanyi.html
# -*- coding: utf-8 -*-
# @Time : 2022/10/15 20:03
# @Author : 楚楚
# @File : 09bs4解析实例.py
# @Software: PyCharm
import requests
from bs4 import BeautifulSoup
import os
import time
if not os.path.exists("./txt"):
os.mkdir('./txt')
# 对首页中的页面数据进行爬取
url = "https://www.shicimingju.com/book/sanguoyanyi.html"
headers = {
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36"
}
page_text = requests.get(url=url, headers=headers)
page_text.encoding='utf-8'
# 在首页中解析出章节的标题和详情页的url
soup = BeautifulSoup(page_text.text, 'lxml')
li_list = soup.select(".book-mulu > ul > li")
file=open('./txt/三国演义.txt','a+',encoding='utf-8')
for li in li_list:
title = li.a.string
file.write(title+'\n')
content_url = 'https://www.shicimingju.com' + li.a['href']
print(content_url)
# 对详情页发起发起发起请求
content = requests.get(url=content_url, headers=headers)
content.encoding='utf-8'
content_soup=BeautifulSoup(content.text)
detail=content_soup.find_all('div',class_='chapter_content')[0].text
file.write(detail)
print("title:"+title)
time.sleep(0.5)
异常处理:
- TypeError: object of type ‘Response’ has no len() 这是为何?
- Python "ResultSet object has no attribute ‘%s’. 问题解决
- ModuleNotFoundError: No module named 'certifi’问题
xpath解析
xpath解析:最常用且最便捷高效的一种解析方式
xpath解析原理:
- 实例化一个etree对象,且需要将被解析的页面源码数据加载到该对象中
- 调用etree对象中的xpath方法结合xpath表达式实现标签的定位和内容的捕获
环境的安装:pip install lxml
实例化一个etree对象(from lxml import etree):将本地的html文档中的源码数据加载到etree对象中:etree.parse(filePath);从互联网上获取的源码数据加载到该对象中:etree.HTML(page_text)
xpath(‘xpath表达式’)
xpath表达式:
- /:表示从根节点开始定位,表示的是一个层级
- //:表示的是多个层级;可以表示从任意位置开始
- 属性定位://div[@class=‘className’]
- 索引定位://div[@class=“className”]/p[3](索引是从1开始的)
- 取文本
- /text():content=tree.xpath(‘//div[@class=“card”]//li[5]/a/text()’)[0]
- /text():获取的是标签中直系的文本内容
- //text():获取的是标签中所有的文本内容
- 取属性
- /@attrName img/@src
- content=tree.xpath(“//div[@class=‘card’]/img/@src”)
from lxml import etree
# 实例化etree对象,且将别解析的源码加载到该对象中
tree=etree.parse('./layout.html')
# content:Element对象
content=tree.xpath('/html/head/title')
# 属性定位
content_attr=tree.xpath('//div[@class="card"]')
# 索引定位
content_index=tree.xpath('//div[@class="card"]/p[0]')
print(content)
print(content_attr)
print(content_index)
实例:千千音乐
url:https://music.91q.com/songlist/295822
# -*- coding: utf-8 -*-
# @Time : 2022/10/15 21:38
# @Author : 楚楚
# @File : 11二手房.py
# @Software: PyCharm
import requests
from lxml import etree
url = "https://music.91q.com/songlist/295822"
headers = {
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36"
}
# proxies={"https": '222.110.147.50:3128'}
response = requests.get(url=url, headers=headers).text
# 数据解析
tree = etree.HTML(response)
titles = tree.xpath('//div[@class="song-box"]/a/text()')
print(titles)
titles_div = tree.xpath("//div[@class='song-box']")
for div in titles_div:
# 局部解析
titles_div_a = div.xpath("./a/text()")
print(titles_div_a)
xpath路径
xpath通过"路径表达式"(Path Expression)来选择节点。在形式上,"路径表达式"与传统的文件系统非常类似
"."表示当前节点
"…"表示当前节点的父节点
“/”:表示选择根节点
“//”:表示选择任意位置的某个节点
“@”: 表示选择某个属性
xml实例文档:
<bookstore>
<book>
<title lang="eng">Harry Pottertitle>
<price>29.99price>
book>
<book>
<title lang="eng">Learning XMLtitle>
<price>39.95price>
book>
bookstore>
/bookstore :选取根节点bookstore
//book :选择所有 book 子元素,而不管它们在文档中的位置
//@lang :选取所有名为 lang 的属性
xpath的谓语条件:所谓"谓语条件",就是对路径表达式的附加条件。所有的条件,都写在方括号"[]"中,表示对节点进行进一步的筛选
/bookstore/book[1] :表示选择bookstore的第一个book子元素
/bookstore/book[last()] :表示选择bookstore的最后一个book子元素
/bookstore/book[last()-1] :表示选择bookstore的倒数第二个book子元素
/bookstore/book[position() < 3] :表示选择bookstore的前两个book子元素
//title[@lang] :表示选择所有具有lang属性的title节点
//title[@lang=‘eng’] :表示选择所有lang属性的值等于"eng"的title节点
/bookstore/book[price] :表示选择bookstore的book子元素,且被选中的book元素必须带有price子元素
/bookstore/book[price>35.00] :表示选择bookstore的book子元素,且被选中的book元素的price子元素值必须大于35
/bookstore/book[price>35.00]/title:在结果集中选择title子元素
/bookstore/book/price[.>35.00] :表示选择值大于35的"/bookstore/book"的price子元素
通配符:"*“表示匹配任何元素节点、”@*"表示匹配任何属性值。
//* :选择文档中的所有元素节点
/*/* :表示选择所有第二层的元素节点
/bookstore/* :表示选择bookstore的所有元素子节点
//title[@*] :表示选择所有带有属性的title元素
选择多个路径:用"|"选择多个并列的路径
//book/title | //book/price :表示同时选择book元素的title子元素和price子元素
实例:4k图片解析下载
# -*- coding: utf-8 -*-
# @Time : 2022/10/16 9:53
# @Author : 楚楚
# @File : 12图片解析.py
# @Software: PyCharm
import requests
from lxml import etree
import os
if not os.path.exists('./picture'):
os.mkdir("./picture")
url = "https://pic.netbian.com/"
headers = {
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36"
}
response = requests.get(url=url, headers=headers)
# response.encoding='utf-8'
text = response.text
tree = etree.HTML(text)
images = tree.xpath("//div[@class='slist']//li")
for image, i in zip(images, range(1, len(images))):
src = "https://pic.netbian.com" + image.xpath('./a/span/img/@src')[0]
# alt=image.xpath('./a/span/img/@alt')[0]+'.jpg'
name = str(i) + '.jpg'
# 请求图片
picture = requests.get(url=src, headers=headers).content
with open('./picture/'+name,'wb') as file:
file.write(picture)
print(f"{name}下载成功")
7、验证码识别
反爬机制:验证码
识别验证码图片中的数据,用于模拟登录操作
8、模拟登录Cookie操作
http/https协议特性:无状态
Cookie的作用:我们在浏览器中,经常涉及到数据的交换,比如登录邮箱,登录一个页面。我们经常会在此时设置30天内记住我,或者自动登录选项。那么它们是怎么记录信息的呢,答案就是今天的主角cookie了, Cookie是由HTTP服务器设置的,保存在浏览器中,但HTTP协议是一种无状态协议,在数据交换完毕后,服务器端和客户端的链接就会关闭,每次交换数据都需要建立新的链接。
session机制采用的是在服务器端保持状态的方案,而cookie机制则是在客户端保持状态的方案,cookie又叫会话跟踪机制。打开一次浏览器到关闭浏览器算是一次会话。HTTP协议是一种无状态协议,在数据交换完毕后,服务器端和客户端的链接就会关闭,每次交换数据都需要建立新的链接。此时,服务器无法从链接上跟踪会话。
cookie分为会话cookie和持久cookie,会话cookie是指在不设定它的生命周期expires时的状态,前面说了,浏览器的开启到关闭就是一次会话,当关闭浏览器时,会话cookie就会跟随浏览器而销毁。当关闭一个页面时,不影响会话cookie的销毁。持久cookie则是设定了它的生命周期expires,此时,cookie像商品一样,有个保质期,关闭浏览器之后,它不会销毁,直到设定的过期时间。对于持久cookie,可以在同一个浏览器中传递数据,比如,你在打开一个淘宝页面登陆后,你在点开一个商品页面,依然是登录状态,即便你关闭了浏览器,再次开启浏览器,依然会是登录状态。
session会话对象:
- 可以进行请求的发送
- 如果请求过程中产生了cookie,则该cookie会被自动存储/携带在该session对象中
import requests
session=requests.Session()
# 使用session进行post请求的发送
# 使用携带cookie的session对象进行get请求的发送
9、代理IP
代理的作用:破解封IP这种反爬机制
代理(代理服务器)的作用:
- 突破自身IP访问的限制
- 隐藏自身真实IP
代理IP的类型:
- http:应用到http协议对应的url中
- https:应用到https协议对应的url中
代理ip的匿名度:
- 透明:服务器知道了该次请求使用了代理,也知道请求对应的真实ip
- 匿名:知道使用了代理,不知道真实ip
- 高匿:不知道使用了代理,更不知道真实的ip
参考文献
1、关于正则表达式中的.*,.*?,.+?的理解
2、xpath路径表达式
3、Cookie的原理、作用,区别以及使用