Requests库
Requests库的7个主要方法
方法 | 说明 |
---|---|
requests.request() | 构造请求 |
requests.get() | 获取网页的主要方法 |
requests.head() | 获取网页头信息 |
requests.post() | 向网页提交POST请求 |
requests.put() | 向网页提交PUT请求 |
requests.patch() | 向网页提交局部修改请求 |
requests.delete() | 向网页提交删除请求 |
request()方法
requests.request(method, url, **kwargs) #完整参数
params:字典或字节序列,作为参数增加到url中
kv = {'key1': 'value1', 'key2': 'value2'}
r = requests.request('GET', 'http://python123.io/ws', params=kv)
print(r.url)
#http://python123.io/ws?key1=value1&key2=value2
data:字典、字节序列或文件对象,作为Request的内容
kv = {'key1': 'value1', 'key2': 'value2'}
r = requests.request('POST', 'http://python123.io/ws', data=kv)
body = '主体内容'
r = requests.request('POST', 'http://python123.io/ws', data=body)
json:JSON格式数据,作为Request的内容
kv = {'key1': 'value1'}
r = requests.request('POST', 'http://python123.io/ws', json=kv)
headers:字典,HTTP定制头
hd = {'user-agent': 'Chrome/10'}
r = requests.request('POST', 'http://python123.io/ws', headers=hd)
其他
cookies:字典或CookieJar,Request中的cookie
auth:元组,支持HTTP认证功能
files:字典类型,传输文件
timeout:设置超时时间,秒为单位
proxies:字典类型,设定访问代理服务器,可以增加登录认证
allow_redirects:True/False,重定向开关
stream:True/False,获取内容立即下载开关
fs = {'file': open('data.xls', 'rb')}
r = requests.request('POST', 'http://python123.io/ws', files=fs)
pxs = {'http': 'http://user:pass@10.10.10.1:1234' #用户名和密码的设置
'https': 'http://10.10.10.1:4321' } #代理服务器IP地址
r = requests.request('GET', 'http://www.baidu.com', proxies=pxs)
get()方法
通过给定url
,构造一个向服务器请求资源的Request
对象
返回一个包含服务器资源的Response
对象,这里用r
表示
r = requests.get(url)
requests.get(url, params=None, **kwargs) #完整参数
Response对象
import requests
r = requests.get("http://www.baidu.com)
print(r.status_code)
属性 | 说明 |
---|---|
r.status_code | HTTP请求返回的状态,只有200表示连接成功 |
r.text | HTTP响应内容的字符串形式,即url对应的页面内容 |
r.encoding | 从HTTP header中猜测的响应内容编码方式 |
r.apparent_encoding | 从内容中分析出的响应内容编码方式 |
r.content | HTTP响应内容的二进制形式 |
Requests库的异常
异常 | 说明 |
---|---|
requests.ConnectionError | 网络连接错误异常 |
requests.HTTPError | HTTP错误异常 |
requests.URLRequired | URL缺失异常 |
requests.TooManyRedirects | 超过最大重定向次数 |
requests.ConnecTimeout | 连接远程服务器超时异常 |
requests.Timeout | 请求URL超时 |
r.raise_for_status()
:如果不是200,产生异常requests.HTTPError
通用代码框架
import requests
def getHTMLText(url):
try:
r = requests.get(url, timeout=30)
r.raise_for_status() #如果状态不是200,引发HTTPError异常
r.encoding = r.apparent_encoding
return r.text
except:
return "产生异常"
if __name__ == "__main__":
url = "http://www.baidu.com"
print(getHTMLText(url))
__name__
是一个系统变量。假如当前模块是主模块(也就是调用其他模块的模块),那么此模块名字就是__main__
,通过if
判断这样就可以执行“__mian__
”后面的主函数内容;假如此模块是被import
的,则此模块名字为文件名字(不加后面的.py)
,通过if
判断这样就会跳过“__mian__
”后面的内容
实例
爬取京东商品信息
import requests
url = "https://item.jd.com/100008348546.html"
try:
r = requests.get(url)
r.raise_for_status()
r.encoding = r.apparent_encoding
print(r.text[:2000])
except:
print("爬取失败")
修改头部信息,模拟浏览器访问
import requests
url = "https://www.amazon.cn/gp/product/B01M8L5Z3Y"
try:
kv = {'user-agent': 'Mozilla/5.0'}
r = requests.get(url, headers=kv)
r.raise_for_status()
r.encoding = r.apparent_encoding
print(r.text[1000:2000])
except:
print("爬取失败")
关键词提交
百度关键词接口:
http://www.baidu.com/s?wd=keyword
import requests
keyword = "Python"
try:
kv = {'wd': keyword}
r = requests.get("http://www.baidu.com/s", params=kv)
print(r.request.url)
r.raise_for_status()
print(len(r.text))
except:
print("爬取失败")
网络图片爬取
import requests
import os
url = "http://image.nationalgeographic.com.cn/2017/0211/20170211061910157.jpg"
root = "C://Users/小涛/Pictures/Saved Pictures/"
path = root + url.split('/')[-1]
try:
if not os.path.exists(root):
os.mkdir(root)
if not os.path.exists(path):
r = requests.get(url)
with open(path, 'wb') as f:
f.write(r.content)
print("文件保存成功")
else:
print("文件已存在")
except:
print("爬取失败")
IP地址归属地自动查询
import requests
url = "http://m.ip138.com/ip.asp?ip="
try:
r = requests.get(url + '222.41.155.45')
r.raise_for_status()
r.encoding = r.apparent_encoding
print(r.text[-500:])
except:
print("爬取失败")
Beautiful Soup库
import requests
from bs4 import BeautifulSoup
url = "https://python123.io/ws/demo.html"
r = requests.get(url)
demo = r.text
soup = BeautifulSoup(demo, "html.parser")
print(soup.prettify())
Beautiful Soup类基本元素
基本元素 | 说明 |
---|---|
Tag | 标签,最基本的信息组织单元,用<> 和</> 标明开头和结尾 |
Name | 标签的名字,<p>...</p> 的名字是'p' ,格式:<tag>.name |
Attributes | 标签的属性,字典形式组织,格式:<tag>.attrs |
NavigableString | 标签内非属性字符串,<>...</> 中字符串,格式<tag>.string |
Comment | 标签内字符串的注释部分 |
标签树的遍历
下行遍历
属性 | 说明 |
---|---|
.contents | 子节点的列表,将<tag> 所有儿子结点存入列表 |
.children | 子节点的迭代类型,与.contents 类似,用于循环遍历儿子节点 |
.descendants | 子孙结点的迭代类型,包含所有子孙节点,用于循环遍历 |
遍历儿子节点
for child in soup.body.children:
print(child)
遍历子孙结点
for child in soup.body.descendants:
print(child)
上行遍历
属性 | 说明 |
---|---|
.parent | 节点的父亲标签 |
.parents | 节点先辈标签的迭代类型,用于循环遍历先辈节点 |
for parent in soup.a.parents:
if parent is None:
print(parent)
else:
print(parent.name)
平行遍历
平行遍历发生在用一个父亲节点下
属性 | 说明 |
---|---|
.next_sibling | 返回按照HTML文本顺序的下一个平行节点标签 |
.previous_sibling | 返回按照HTML文本顺序的上一个平行节点标签 |
.next_siblings | 迭代类型,返回按照HTML文本顺序的后序所有平行节点标签 |
.previous_siblings | 迭代类型,返回按照HTML文本顺序的前续所有平行节点标签 |
遍历后续节点
for sibling in soup.a.next_siblings:
print(sibling)
遍历前续节点
for sibling in soup.a.previous_siblings:
print(sibling)
信息提取一般方法
提取HTML中所有URL链接
- 搜索到所有的
<a>
标签 - 解析
<a>
标签格式,提取href
后的链接内容
import requests
from bs4 import BeautifulSoup
url = "https://python123.io/ws/demo.html"
r = requests.get(url)
demo = r.text
soup = BeautifulSoup(demo, "html.parser")
for link in soup.find_all('a'):
print(link.get('href'))
<>.find_all
<>.find_all(name, attrs, recursive, string, **kwargs)
返回一个列表类型,存储查找结果
- name:对标签名称的检索字符串
soup.find_all(['a','b']) #同时查找a,b标签
for tag in soup.find_all(True): #显示文档中所有标签名称
print(tag.name)
- attrs:对标签属性值的检索字符串,可标注属性检索
- recursive:是否对子孙全部检索,默认True
- string:
<>...</>
中字符串区域的检索字符串
正则表达式
语法
操作符 | 说明 | 实例 |
---|---|---|
. | 表示任何单个字符 | |
[] | 字符集,对单个字符给出取值范围 | [abc] 表示a、b、c,[a-z] 表示a到z单个字符 |
[^] | 非字符集,对单个字符给出排除范围 | [^abc] 表示非a或b或c的单个字符 |
* | 前一个字符0次或无限次扩展 | abc* 表示ab、abc、abcc、abccc等 |
+ | 前一个字符1次或无限次扩展 | abc+ 表示abc、abcc、abccc等 |
? | 前一个字符0次或1次扩展 | abc? 表示ab、abc |
| | 左右表达式任意一个 | abc|def 表示abc或者def |
{m} | 扩展前一个字符m次 | ab{2}c 表示abbc |
{m,n} | 扩展前一个字符m到n次 | ab{1,2}c 表示abc、abbc |
^ | 匹配字符串开头 | ^abc 表示abc且在一个字符串的开头 |
$ | 匹配字符串的结尾 | abc$ 表示abc且在一个字符串的结尾 |
() | 分组标记,内部只能使用| 操作符 | (abc|def) 表示abc或def |
\d | 数字,等价于[0-9] | |
\w | 单词字符,等价与[A-Za-z0-9_] |
语法实例
正则表达式 | 对应字符串 |
---|---|
P(Y|YT|YTH|YTHO)?N | 'PN','PYN','PYTN','PYTHN','PYTHON' |
PYTHON+ | 'PYTHON','PYTHONN','PYTHONNN'... |
PY[TH]ON | 'PYTON','PYHON' |
PY[^TH]?ON | 'PYON','PYaON','PYbON'... |
PY{:3}N | 'PN','PYN','PYYN','PYYYN' |
^[A-Za-z]+$ | 由26个字母组成的字符串 |
^-?\d+$ | 整数形式的字符串 |
^[0-9]*[1-9][0-9]*$ | 正整数形式的字符串 |
匹配IP地址的正则表达式
0-99:[1-9]?\d
100-199:1\d{2}
200-249:2[0-4]\d
250-255:25[0-5]
(([1-9]?\d|1\d{2}|2[0-4]\d|25[0-5]]).){3}([1-9]?\d|1\d{2}|2[0-4]\d|25[0-5]])
Re库
正则表达式的表示类型采用
raw string
类型,不会把转义字符理解为转义字符
主要功能函数
函数 | 说明 |
---|---|
re.search() | 在一个字符串中搜索匹配正则表达式的第一个位置,返回match对象 |
re.match() | 从一个字符串的开始位置起匹配正则表达式,返回match对象 |
re.findall() | 搜索字符串,以列表类型返回全部能匹配的字符串 |
re.split() | 将一个字符串按照正则表达式匹配结果进行分割,返回列表类型 |
re.finditer() | 搜索字符串,返回一个匹配结果的迭代类型,每个迭代元素是match对象 |
re.sub() | 在一个字符串中替换所有匹配正则表达式的子串,返回替换后的字符串 |
- re.search(pattern, string, flags=0)
pattern
:正则表达式的字符串
string
:待匹配字符串
flags
:正则表达式使用时的控制标记re.I
:忽略大小写re.M
:正则表达式中的^
操作符能够将给定字符串的每行当作匹配开始re.S
:正则表达式中的.
操作符能够匹配所有字符(默认匹配除换行符外的所有字符)
import re
match = re.search(r'[1-9]\d{5}', 'BIT 100081')
if match:
print(match.group(0))
#re.match(pattern, string, flags=0)
match = re.match(r'[1-9]\d{5}', '100081 BIT')
if match:
print(match.group(0))
#re.findall(pattern, string, flags=0)
ls = re.findall(r'[1-9]\d{5}', 'BIT100081 TSU100084')
print(ls)
#re.split(pattern, string, maxsplit=0, flags=0)
#maxsplit:最大分割数,剩余部分作为最后一个元素输出
ls = re.split(r'[1-9]\d{5}', 'BIT100081 TSU100084', maxsplit=1)
print(ls)
#re.finditer(pattern, string, flags=0)
for m in re.finditer(r'[1-9]\d{5}', 'BIT100081 TSU100084'):
if m:
print(m.group(0))
#re.sub(pattern, repl, string, count=0, flags=0)
#repl:替换匹配字符串的字符串
#count:匹配的最大替换次数
print(re.sub(r'[1-9]\d{5}', ':zipcode', 'BIT100081 TSU100084'))
re.compile(pattern, flags=0)
:将正则表达式的字符串形式编译成正则表达式对象
pat = re.compile(r'[1-9]\d{5}')
rst = pat.search('BIT 100081')
match对象
match对象的属性
属性 | 说明 |
---|---|
.string | 待匹配的文本 |
.re | 匹配时使用的pattern对象(正则表达式) |
.pos | 正则表达式搜索文本的开始位置 |
.endpos | 正则表达式搜索文本的结束位置 |
match对象的方法
方法 | 说明 |
---|---|
.group(0) | 获得匹配后的字符串 |
.start() | 匹配字符串在原始字符串的开始位置 |
.end() | 匹配字符串在原始字符串的结束位置 |
.span() | 返回(.start(),.end()) (元组类型) |
Re库的贪婪匹配和最小匹配
Re库默认采用贪婪匹配,即输出匹配最长的子串
match = re.search(r'PY.*N', 'PYNTHON')
最小匹配
操作符 | 说明 |
---|---|
*? | 前一个字符0次或无限次扩展,最小匹配 |
+? | 前一个字符1次或无限次扩展,最小匹配 |
?? | 前一个字符0次或1次扩展,最小匹配 |
{m,n}? | 扩展前一个字符m至n次,最小匹配 |
match = re.search(r'PY.*?N', 'PYNTHON')
实例1:大学排名定向爬虫
import requests
import bs4
from bs4 import BeautifulSoup
def getHTMLText(url):
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):
soup = BeautifulSoup(html, "html.parser")
for tr in soup.find('tbody').children: #在tbody标签中遍历tr标签
if isinstance(tr, bs4.element.Tag): #过滤tr标签
tds = tr('td') #将需要的td标签放入列表中
ulist.append([tds[0].string, tds[1].string, tds[3].string])
def printUnivList(ulist, num):
tplt = "{0:^10}\t{1:{3}^10}\t{2:^10}"
print(tplt.format("排名", "学校名称", "总分", chr(12288)))
for i in range(num):
u = ulist[i]
print(tplt.format(u[0], u[1], u[2], chr(12288)))
print("Suc" + str(num))
def main():
uinfo = []
url = 'http://www.zuihaodaxue.cn/zuihaodaxuepaiming2019.html'
html = getHTMLText(url)
fillUnivList(uinfo, html)
printUnivList(uinfo, 20)
main()
实例2:淘宝商品信息定向爬虫
- 提交商品搜索请求,循环获取页面
- 对于每个页面,提取商品名称和价格信息
- 输出
需要替换
http request header
来欺骗浏览器实现登录
参考
import requests
import re
def getHTMLText(url):
try:
kv = {'user-agent': 'Mozilla/5.0', 'cookie': 'cna=zIRRFl/4eTsCAXJXhTgb4lGt; t=5160a85424fe174c6483c21b457a7924; UM_distinctid=16e676c7723458-096acb70b9ae84-7711a3e-144000-16e676c772420e; thw=cn; miid=637631782000795542; hng=CN%7Czh-CN%7CCNY%7C156; tracknick=%5Cu5BFB%5Cu627E%5Cu5927%5Cu6D77%5Cu7684%5Cu8FB9%5Cu9645; tg=0; enc=xfF4HAm8jO%2BUszxmV8ICy3Q30oEGEP2RneojvYIePy4qijus9ou9uroNbTbO7LawB7PG6GFSa30ENlpKC7s2qg%3D%3D; lgc=%5Cu5BFB%5Cu627E%5Cu5927%5Cu6D77%5Cu7684%5Cu8FB9%5Cu9645; mt=ci=-1_0; v=0; cookie2=1c39d4f8be33f0c008c9ba12cc683f51; _tb_token_=eb58e3914637b; _samesite_flag_=true; dnk=%5Cu5BFB%5Cu627E%5Cu5927%5Cu6D77%5Cu7684%5Cu8FB9%5Cu9645; _mw_us_time_=1581257284309; unb=3298721164; uc1=cookie14=UoTUO8IXvg05xA%3D%3D&cookie15=WqG3DMC9VAQiUQ%3D%3D&pas=0&cookie16=UIHiLt3xCS3yM2h4eKHS9lpEOw%3D%3D&tag=8&existShop=false&cookie21=Vq8l%2BKCLjA%2Bl&lng=zh_CN; uc3=lg2=WqG3DMC9VAQiUQ%3D%3D&id2=UNJYTSb8qyy%2BXQ%3D%3D&nk2=sr8Vs0VugPzEVg7lNik%3D&vt3=F8dBxdsanYORhSwLwS8%3D; csg=f90609f8; cookie17=UNJYTSb8qyy%2BXQ%3D%3D; skt=2c23f554dc07c90e; existShop=MTU4MTI1NzI4OA%3D%3D; uc4=id4=0%40UgXf7eZnM5SB1%2B%2BDWqIn9B4aoaDK&nk4=0%40sLdrDpuShvdSuCTc9Ehn21Dl2KPIMwvs4g%3D%3D; _cc_=U%2BGCWk%2F7og%3D%3D; _l_g_=Ug%3D%3D; sg=%E9%99%854e; _nk_=%5Cu5BFB%5Cu627E%5Cu5927%5Cu6D77%5Cu7684%5Cu8FB9%5Cu9645; cookie1=W8DZrI54q0OdcLVx7R3%2BVI4N%2FXEE2FhVBLYSKtrKtE8%3D; isg=BDw8S-C2To8AgXlfYamMSzRUDdruNeBfi2nGQxa9SCcK4dxrPkWw77JTwQmZshi3; l=cBrDvrCcqejNAMBEBOCanurza77OSIRYYuPzaNbMi_5Qy6T_qO7OoSe2NF96VjWd998B4Tn8Nrv9-etkZK066bIpXUJ1.'}
r = requests.get(url, timeout=30, headers=kv)
r.raise_for_status()
r.encoding = r.apparent_encoding
#print(r.text)
return r.text
except:
return ""
def parsePage(ilt, html):
try:
plt = re.findall(r'\"view_price\"\:\"[\d\.]*\"', html)
tlt = re.findall(r'\"raw_title\"\:\".*?\"', html)
for i in range(len(plt)):
price = eval(plt[i].split(':')[1]) #eval:去掉双引号或单引号
title = eval(tlt[i].split(':')[1])
ilt.append([price, title])
except:
print("")
def printGoodList(ilt):
tplt = "{:4}\t{:8}\t{:16}"
print(tplt.format("序号", "价格", "商品名称"))
count = 0
for g in ilt:
count = count+1
print(tplt.format(count, g[0], g[1]))
def main():
goods = '书包'
depth = 3
start_url = 'https://s.taobao.com/search?q=' + goods
infoList = []
for i in range(depth):
try:
url = start_url + '&s=' + str(44*i)
html = getHTMLText(url)
parsePage(infoList, html)
except:
continue
printGoodList(infoList)
main()