Python爬虫
文章目录
request库
import requests
r = requests.get("https://www.icourse163.org")
print(r.status_code) #状态码200,访问成功
print(type(r)) #<class 'requests.models.Response'>
#编码方式
print(r.encoding) #charset字段获得,不存在默认为ISO-8859-1
print(r.apparent_encoding) #备选编码,通过分析内容推断出编码
r.encoding = 'utf-8' #设置编码
print(r.text) #html内容
print(r.headers) #http请求头
print(r.content) #用于解析图片等内容
request对象属性
r.status_code | HTTP请求的返回状态,200表示连接成功,404表示失败 |
r.text | HTTP响应内容的字符串形式,即url对应的页面内容 |
r.encoding | 从HTTP header中猜测的响应内容编码方式 |
r.apparent_encoding | 从内容中分析出的响应内容编码方式(备选编码方式) |
r.content | HTTP响应内容的二进制形式 |
request对象方法
方法说明
requests.request() | 构造一个请求,支撑以下各方法的基础方法 |
requests.get() | 获取HTML网页的主要方法,对应于HTTP的GET |
requests.head() | 获取HTML网页头信息的方法,对应于HTTP的HEAD |
requests.post() | 向HTML网页提交POST请求的方法,对应于HTTP的POST |
requests.put() | 向HTML网页提交PUT请求的方法,对应于HTTP的PUT |
requests.patch() | 向HTML网页提交局部修改请求,对应于HTTP的PATCH |
requests.delete() | 向HTML页面提交删除请求,对应于HTTP的DELETE |
通用代码框架
import requests
import time
def getHTMLText(url):
"""
获取指定 URL 的 HTML 内容
Parameters:
url (str): 要获取 HTML 内容的 URL
Returns:
str: 成功获取到的 HTML 内容,如果请求失败则返回 "产生异常"
"""
try:
r = requests.get(url,timeout=1) # 发送 HTTP GET 请求,可以通过timeout=1设置超时时间为 1 秒
r.raise_for_status() # 检查是否出现 HTTP 请求错误
r.encoding = r.apparent_encoding # 根据响应内容自动设置编码
return r.text # 返回 HTML 内容
except:
return "产生异常"
if __name__ == "__main__":
url1 = "https://www.baidu.com/"
print(getHTMLText(url1)) # 成功输出HTML内容
url2 = "https://chat.openai.com/"
print(getHTMLText(url2)) # 产生异常
first_time = time.time()
N = 100
for i in range(0,N):
getHTMLText(url1)
last_time = time.time()
print(f'访问{N}次耗时:{last_time-first_time}') #访问100次耗时:9.285466194152832
#可以看到request库性能较低
Robots协议
我们可以在网站的根目录建立一个robots.txt
文件来说明限制爬虫的行为,但是这只是一种公示,不能真正阻止爬虫。
实例
- 当网站对访问用户设备有限制时,我们可以修改
user-agent字段
实现访问。
kv = {'user-agent':'Mozilla/5.0'}
r = requests.get(url,timeout=1,headers=kv)
2.部分网站提供API URL 搜索
百度:http://www.baidu.com/s?wd
360:http://www.so.com/s?q
百度的我没有爬到,这里发下360的爬虫
import requests
url ='http://www.so.com/s'
keyword ='Python'
kv = {'q':keyword}
try:
kv = {'q':keyword}
r= requests.get(url,params = kv)
r.raise_for_status()
r.encoding= r.apparent_encoding
print(r.text)
except:
print('爬取失败')
- 存储
import requests
import os
url ='https://img-blog.csdnimg.cn/f35b24a8c05744dca042a64ef2154c12.png'
root = "H:/pachong/" # 图片保存的根目录
path = root + url.split('/')[-1] # 图片保存的完整路径,使用URL中最后一个/后的文件名
try:
if not os.path.exists(root): # 如果根目录不存在,则创建
os.makedirs(root)
if not os.path.exists(path): # 如果文件不存在,则执行以下操作
r = requests.get(url)
with open(path,'wb') as f: # 以二进制写入模式打开文件
f.write(r.content) # 二进制写入
f.close()
print("文件已保存")
else:
print("文件已存在")
except:
print('爬取失败')
- ip归属地查询
我这里用的是ip38查询
import requests
url ='https://ip38.com/ip.php'
keyword ='ip-address'
kv = {'ip':keyword}
try:
kv = {'ip':keyword}
r= requests.get(url,params = kv)
r.raise_for_status()
r.encoding= r.apparent_encoding
print(r.request.url)
print(r.text)
except:
print('爬取失败')
Beautiful Soup
- 默认编码为
UTF-8
基本使用
from bs4 import BeautifulSoup
import requests
url = 'https://baike.baidu.com/item/%E5%88%9D%E9%9F%B3%E6%9C%AA%E6%9D%A5/8231955'
try:
r = requests.get(url)
demo = r.text
soup = BeautifulSoup(demo,"html.parser") #html解析
print(soup.prettify()) #添加缩进和换行符,使得文档的结构更加清晰易读。
except:
print("error")
基本元素
基本元素说明
Tag | 标签,最基本的信息组织单元,分别用<>和</>标明开头和结尾 |
Name | 标签的名字, … 的名字是’p’,格式:.name |
Attributes | 标签的属性,字典形式组织,格式:.attrs |
NavigableString | 标签内非属性字符串,<>…</>中字符串,格式:.string |
Comment | 标签内字符串的注释部分,一种特殊的Comment类型 |
from bs4 import BeautifulSoup
import requests
url = 'https://python123.io/ws/demo.html'
try:
r = requests.get(url)
demo = r.text
soup = BeautifulSoup(demo,"html.parser")
#print(soup.prettify())
print(soup.title)
tag = soup.a
print(tag)
print(tag.name)
print(tag.parent.name)
print(tag.parent.parent.name)
print(tag.attrs)
print(tag.attrs['href'])
print(type(tag))
tag1 = soup.p
print(soup.p.string)
print(type(soup.p.string))
print(soup.prettify())
newsoup = BeautifulSoup("<b><!--This is a comment--></b><p>This is not a comment</p>","html.parser")
print(newsoup.b.string)
print(type(newsoup.b.string))
print(newsoup.p.string)
print(type(newsoup.p.string))
# 注意通过`.string`输出不会指出该类型为`comment`,所以我们要通过`type()`来判断是否为注释
except:
print("error")
HTML内容遍历
分为下行遍历
、上行遍历
和平行遍历
,直接看代码更容易理解
from bs4 import BeautifulSoup # 导入BeautifulSoup库,用于解析HTML
import requests # 导入requests库,用于发送HTTP请求
url = 'https://python123.io/ws/demo.html' # 目标页面URL
try:
r = requests.get(url) # 发送GET请求获取页面内容
demo = r.text # 获取页面文本
soup = BeautifulSoup(demo, "html.parser") # 解析页面
# 打印<head>标签的子节点
print(soup.head.contents)
# 打印<body>标签的子节点
print(soup.body.contents)
print("--------------------------------------------------------------------下行遍历---------------------------------------------------------------")
# 使用.children属性遍历<body>标签的子节点
for child in soup.body.children:
print(child)
# 打印<body>标签的子节点数量
print(len(soup.body.contents))
# 打印<body>标签的每个子节点
for i in range(len(soup.body.contents)):
print(soup.body.contents[i])
print("--------------------------------------------------------------------------------------------------------------------------------------------")
print("--------------------------------------------------------------------上行遍历---------------------------------------------------------------")
# 遍历<a>标签的所有父节点
for parent in soup.a.parents:
if parent is None:
print(parent)
else:
# 打印父节点的名称
print(parent.name)
print("--------------------------------------------------------------------------------------------------------------------------------------------")
print("--------------------------------------------------------------------平行遍历---------------------------------------------------------------")
# 打印<p>标签的文本内容
print(soup.p)
# 遍历<p>标签之后的所有兄弟节点
for sibling in soup.p.next_siblings:
print(sibling)
print("#########################################################################################################################################")
# 遍历<p>标签之前的所有兄弟节点
for sibling in soup.p.previous_siblings:
print(sibling)
print("--------------------------------------------------------------------------------------------------------------------------------------------")
except Exception as e:
# 捕获异常并打印错误信息
print("error:", e)
信息组织与提取
信息标记的三种方式
XML (eXtensible Markup Language)
- 最早的通用信息标记语言,可扩展性好,但繁琐
- Internet上的信息交互与传递
<person>
<firstName>Tian</firstName>
<lastName>Song</lastName>
<address>
<streetAddr>中关村南大街5号</streetAddr>
<city>北京市</city>
<zipcode>100081</zipcode>
</address>
<prof>Computer System</prof><prof>Security</prof>
</person>
JSON (JavaScript Object Notation)
-
信息有类型,适合程序处理(js),较XML简洁
-
移动应用云端和节点的信息通信,无注释
{
“firstName” : “Tian” ,
“lastName” : “Song” ,
“address” : {
“streetAddr” : “中关村南大街5号” ,
“city” : “北京市” ,
“zipcode” : “100081”
} ,
“prof” : [ “Computer System” , “Security” ]
YAML (YAML Ain't Markup Language)
-
信息无类型,文本信息比例最高,可读性好
-
各类系统的配置文件,有注释易读
firstName : Tian
lastName : Song
address :
streetAddr : 中关村南大街5号
city : 北京市
zipcode : 100081
prof :
‐Computer System
‐Security
信息提取
from bs4 import BeautifulSoup
import requests
import re
url = 'https://python123.io/ws/demo.html'
try:
r = requests.get(url) # 发送GET请求获取页面内容
demo = r.text # 获取页面文本
soup = BeautifulSoup(demo, "html.parser") # 使用BeautifulSoup解析页面
# 查找所有<a>标签并打印其href属性值
print("-----------------")
for link in soup.find_all('a'):
print(link.get('href'))
print("-----------------")
print()
# 查找所有标签名,并打印出每个标签的名称
print("-----------------")
for tag in soup.find_all(True):
print(tag.name)
print("-----------------")
print()
# 使用正则表达式查找标签名中含有'b'的所有标签,并打印出标签名
print("-----------------")
for tag in soup.find_all(re.compile('b')):
print(tag.name)
print("-----------------")
print()
# 根据id属性值查找标签,并打印出符合条件的标签
print("-----------------")
for tag in soup.find_all(id='link1'):
print(tag)
print("-----------------")
for tag in soup.find_all(id=re.compile('link')):
print(tag)
print("-----------------")
print()
# 查找所有<a>标签及其子孙节点,并打印出每个标签
print("-----------------")
print(soup.find_all('a'))
print("-----------------")
# 只在当前层级查找<a>标签,并打印出每个标签
print(soup.find_all('a', recursive=False))
print("-----------------")
print()
# 根据文本内容查找标签,并打印出符合条件的标签
print("-----------------")
print(soup.find_all(string="Basic Python"))
print("-----------------")
print(soup.find_all(string=re.compile('Python')))
print("-----------------")
except Exception as e:
# 捕获异常并打印错误信息
print("error:", e)
拓展方法
方法说明
- <>.find() 搜索且只返回一个结果,同.find_all()参数
- <>.find_parents() 在先辈节点中搜索,返回列表类型,同.find_all()参数
- <>.find_parent() 在先辈节点中返回一个结果,同.find()参数
- <>.find_next_siblings() 在后续平行节点中搜索,返回列表类型,同.find_all()参数
- <>.find_next_sibling() 在后续平行节点中返回一个结果,同.find()参数
- <>.find_previous_siblings() 在前序平行节点中搜索,返回列表类型,同.find_all()参数
- <>.find_previous_sibling() 在前序平行节点中返回一个结果,同.find()参数
中国大学排名定向爬虫实例
这里使用的是2023版的排名,8年过去网页结构已发生变化,稍加修改还能继续爬取【软科排名】2023年最新软科中国大学排名|中国最好大学排名 (shanghairanking.cn)
from bs4 import BeautifulSoup
import bs4
import requests
def getHTMLText(url):
"""
获取指定URL的HTML文本内容
参数:
url (str): 目标网页的URL
返回:
str: 网页的HTML内容,如果请求失败则返回空字符串
"""
try:
r = requests.get(url, timeout=30)
r.raise_for_status()
r.encoding = r.apparent_encoding
return r.text
except:
return ""
def fillUnivList(ulist, html):
"""
将HTML文本中的大学信息填充到列表中
参数:
ulist (list): 存储大学信息的列表
html (str): 网页的HTML内容
"""
soup = BeautifulSoup(html, "html.parser")
for tr in soup.find('tbody').children:
if isinstance(tr, bs4.element.Tag): #是否为标签元素
tds = tr('td')
a = tds[0].string.strip()
b = tds[1].find('a', class_='name-cn').string.strip() #大学名
c = tds[1].find('p').string.strip() #985还211还是双一流
d = tds[2].text.strip() #地域,不能用stirng,返回了none,这里用text
e = tds[3].text.strip() #类型
f = tds[4].string.strip() #总分,text和string类型都可以
ulist.append([a, b, c, d, e, f])
def printUnivList(ulist, num):
"""
打印大学信息列表
参数:
ulist (list): 存储大学信息的列表
num (int): 打印的大学数量
"""
tplt = "{:^20}\t{:^20}\t{:^20}\t{:^20}\t{:^10}\t{:^10}"
print(tplt.format("排名", "学校名称", "级别", "地域", "类型", "总分"))
for i in range(num):
u = ulist[i]
print(tplt.format(u[0], u[1], u[2], u[3], u[4], u[5]))
def main():
"""
主函数,用于执行爬虫程序
"""
uinfo = []
url = 'https://www.shanghairanking.cn/rankings/bcur/202311'
html = getHTMLText(url)
fillUnivList(uinfo, html)
printUnivList(uinfo, 30)
main()