文章目录
Resuests库入门
request 库
安装方法:
- 管理员权限启动cmd
- 输入>> pip install requests
测试:
- 启动idle
- 输入命令
>>> import requests
>>> r = requests.get("http://www.baidu.com")
>>> r.status_code
200
>>> r.encoding = 'utf-8'
>>> r.text
HTTP协议与Requests库的7个主要方法
HTTP协议
HTTP,Hypertext Transfer Protocol,超文本传输协议。
HTTP是一个基于“请求与相应”模式的、无状态的应用层协议。
HTTP协议采用URL作为定位网络资源的标识。
HTTP协议的URL格式 http://host[:port][path]
host:合法的Internet主机域名或IP地址
port:端口号,缺省端口为80
path:请求资源的路径
HTTP URL实例
http://www.bit.edu.cn
http://220.181.11.188/duty
HTTPURL的理解:
URL是通过HTTP协议存取资源的Internet路径,一个URL对应一个数据资源。
HTTP协议对资源的操作
方法 | 说明 |
---|---|
GET | 请求获取URL位置的资源 |
HEAD | 请求获取URL位置资源的响应消息报告,即获得该资源的头部信息 |
POST | 请求向URL位置的资源后附加新的数据 |
PUT | 请求向URL位置存储一个资源,覆盖原URL位置的资源 |
PATCH | 请求局部更新URL位置资源,即改变该处资源的部分内容 |
DELETE | 请求删除URL位置存储的资源 |
理解patch和put的区别
假设URL位置有一组数据UserInfo,包含UserID、UserName等20个字段。
需求:用户修改了UserName,其他不变。
采用patch,仅向URL提交UserName的局部更新请求。
采用PUT,必须将所有20个字段一并提交到url,未提交字段被删除
patch节省网络带宽
Requests库的7个主要方法
方法 | 说明 |
---|---|
reuqests.request() | 构造一个请求支撑以下各方法的基础方法 |
reuqests.get() | 获取html页面的主要方法,对应于http的get |
reuqests.head() | 获取http网页头信息的方法,对应于http的Head |
reuqests.post() | 向http页面提交post请求,对应http的head |
reuqests.put() | 向http页面提交put请求,对应http的post |
reuqests.patch | 向html页面提交局部修改请求,对应于http的patch |
reuqests.delete() | 向html页面提交删除请求,对应于http的delete |
HTTP协议与Requests库方法
HTTP协议方法 | Requests库方法 | 功能一致性 |
---|---|---|
GET | reuqests.get() | 一致 |
HEAD | reuqests.head() | 一致 |
POST | reuqests.post() | 一致 |
PUT | reuqests.put() | 一致 |
PATCH | reuqests.patch | 一致 |
DELETE | reuqests.delete() | 一致 |
requests.request(method,url,**kwargs)
- method:请求方式,对应get/put/post等7种
r = requests.request(‘GET’,url,**kwargs)
r = requests.request('HEAD,url,**kwargs)
r = requests.request(‘POST’,url,**kwargs)
r = requests.request(‘PUT’,url,**kwargs)
r = requests.request(‘PATCH’,url,**kwargs)
r = requests.request(‘DELETE’,url,**kwargs)
r = requests.request(‘OPTIONS’,url,**kwargs)
- url:获取页面的url连接
- **kwargs:控制访问参数,共13个,均为可选项
- params:字典或字节序列,作为参数增加到url中
>>> kv ={'key1':'value1','key2':'value2'}
>>> r = requests.request('GET','http://python123.io/ws',params = kv)
>>> print(r.url)
https://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:字典类型,传输文件
>>> fs = {'file':open('C:\\Users\\admin\\Desktop\\data.xlsx','rb')}
>>> r = requests.request('POST','http://python123.io/ws',files=fs)
- timeout:设定的超时时间,以秒为单位
>>> r = requests.request('GET','http://www.baidu.com',timeout=10)
- proxies:字典类型,设定访问代理服务器,可以增加登录认证
>>> pxs = {'http':'http://user:pass@10.10.10.1:1234','https':'https://10.10.10.14321'}
>>> r = requests.request('GET','http://www.baidu.com',proxies=pxs)
-
allow_redirects:True/False,默认为True,重定向开关
-
stream:True/False,默认为True,获取内容立即下载开关
-
verify:True/False,默认为True,认证SSL证书开关
-
cert:保存本地ssl证书路径
r = reuqests.get(url)
构造一个向服务器请求资源的Request对象(Request)
返回一个包含服务资源的Response对象(Response)
主要参数列表
requests.get(url,params = None,**kwargs)
- url:你获取页面的url连接
- params: url中的额外参数,字典或字节流格式,可选
- *kwargs:12个控制访问的参数(同requests.request()函数除params*外的字段)
'''源码'''
def get(url, params=None, **kwargs):
r"""Sends a GET request.
:param url: URL for the new :class:`Request` object.
:param params: (optional) Dictionary, list of tuples or bytes to send
in the query string for the :class:`Request`.
:param \*\*kwargs: Optional arguments that ``request`` takes.
:return: :class:`Response <Response>` object
:rtype: requests.Response
"""
kwargs.setdefault('allow_redirects', True)
return request('get', url, params=params, **kwargs)
**Response对象:**包含爬虫返回的全部内容
>>> import requests
>>> r = requests.get("http://www.baidu.com")
>>> print(r.status_code)
200
>>> type(r)
<class 'requests.models.Response'>
>>> r.headers
{'Cache-Control': 'private, no-cache, no-store, proxy-revalidate, no-transform', 'Connection': 'keep-alive', 'Content-Encoding': 'gzip', 'Content-Type': 'text/html', 'Date': 'Wed, 18 Dec 2019 03:34:09 GMT', 'Last-Modified': 'Mon, 23 Jan 2017 13:27:29 GMT', 'Pragma': 'no-cache', 'Server': 'bfe/1.0.8.18', 'Set-Cookie': 'BDORZ=27315; max-age=86400; domain=.baidu.com; path=/', 'Transfer-Encoding': 'chunked'}
Response对象的属性
属性 | 说明 |
---|---|
r.status_code | http请求的返回状态,200表示连接成功,404表示失败 |
r.text | http响应内容的字符串形式,即url对应的页面内容 |
r.encoding | 从http header中猜测响应内容编码方式 |
r.apparent_encoding | 从内容中分析出的相应内容编码方式(备选编码方法) |
r.content | Http相应内容的二进制形式 |
>>> r.encoding
'ISO-8859-1'
>>> r.apparent_encoding
'utf-8'
>>> r.encoding='utf-8'
>>> r.text
理解Response的编码
属性 | 说明 |
---|---|
r.encoding | 从http header中猜测响应内容编码方式 |
r.apparent_encoding | 从内容中分析出的相应内容编码方式(备选编码方法) |
r.encoding:如果header中不存在charset,则认为编码为’ISO-8859-1’
r.apparent_encoding:根据网页内容分析出的编码方式
Requests库的head(url,**kwargs)方法
**kwargs与requests.request方法完全一样
>>> r = requests.head('http://httpbin.org/get')
>>> r.headers
{'Access-Control-Allow-Credentials': 'true', 'Access-Control-Allow-Origin': '*', 'Content-Encoding': 'gzip', 'Content-Type': 'application/json', 'Date': 'Wed, 18 Dec 2019 05:04:05 GMT', 'Referrer-Policy': 'no-referrer-when-downgrade', 'Server': 'nginx', 'X-Content-Type-Options': 'nosniff', 'X-Frame-Options': 'DENY', 'X-XSS-Protection': '1; mode=block', 'Connection': 'keep-alive'}
>>> r.text
''
Resquests库的Post(url,data=None,json=None,**kwargs)方法
- url:拟更新页面的url
- data:字典、字节序列或文件,Request的内容
- JSON格式的数据,Request的内容
- **kwargs:11个访问控制参数(同requests.request()的控制访问参数去除data和json)
>>> payload = {'key1':'value1','key2':'value2'}
>>> r = requests.post('http://httpbin.org/post',data=payload)
>>> print(r.text)
{
"args": {},
"data": "",
"files": {},
"form": {
"key1": "value1",
"key2": "value2"
},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Content-Length": "23",
"Content-Type": "application/x-www-form-urlencoded",
"Host": "httpbin.org",
"User-Agent": "python-requests/2.22.0"
},
"json": null,
"origin": "124.88.99.162, 124.88.99.162",
"url": "https://httpbin.org/post"
}
向URL POST一个字典,自动编码为form(表单)
>>> r = requests.post('http://httpbin.org/post',data = 'ABC')
>>> print(r.text)
{
"args": {},
"data": "ABC",
"files": {},
"form": {},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Content-Length": "3",
"Host": "httpbin.org",
"User-Agent": "python-requests/2.22.0"
},
"json": null,
"origin": "124.88.99.162, 124.88.99.162",
"url": "https://httpbin.org/post"
}
向URL POST一个字符串,自动编码为data
Requests库的put(url,data=None,**kwargs)方法
- url:拟更新页面的url连接
- data:字典、字节序列或文件,Request的内容
- **kwargs:12个控制访问的参数
>>> payload = {'key1':'value1','key2':'value2'}
>>> r = requests.put('http://httpbin.org/put',data=payload)
>>> print(r.text)
{
"args": {},
"data": "",
"files": {},
"form": {
"key1": "value1",
"key2": "value2"
},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Content-Length": "23",
"Content-Type": "application/x-www-form-urlencoded",
"Host": "httpbin.org",
"User-Agent": "python-requests/2.22.0"
},
"json": null,
"origin": "124.88.99.162, 124.88.99.162",
"url": "https://httpbin.org/put"
}
requests.patch(url,data=None,**kwargs)
- url:拟更新页面的url连接
- 字典、字节序列或文件,Request的内容
- **kwargs:12个控制访问参数
request.delete(url,**kwargs)
- url拟删除页面的url连接
- **kwargs:13个控制访问的参数
理解Requests库的异常
Request对象
异常 | 说明 |
---|---|
requests.ConnectionError | 网络连接错误异常,如DNS查询失败、拒绝连接等 |
requests.HTTPError | HTTP错误异常 |
requests.URLRequired | URL缺失异常 |
requests.TooManyRedirects | 超过最大重定向次数,产生重定向异常 |
requests.ConnectTimeout | 远程连接服务器超时 |
requests.Timeout | 请求URL超时,产生超时异常 |
Response对象
异常 | 说明 |
---|---|
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))
小结:Request库入门
一共有七个方法
requests.request()
requests.get()
requests.head()
requests.post()
requests.put()
requests.patch()
requests.delete()
爬取网页的通用代码框架
try:
r = requests.get(url,timeout=30)
r.raise_for_status() # 如果返回的对象状态码不是200,产生一次异常
r.encoding = r.apparent_encoding
return r.text
except:
return "产生异常"
网络爬虫引发的问题
网络爬虫的尺寸
爬取网页 玩转网页 | 爬取网站 爬取系列网站 | 爬取全网 |
---|---|---|
小规模,数据量小 爬取速度不敏感 占90% | 中规模,数据规模较大 爬取速度敏感 | 大规模,搜索引擎 爬取速度关键 |
Request库 | Scrapy库 | 定制开发 |
网络爬虫的“骚扰”!!!
受限于编写水平和目的,网络爬虫将会为web服务器带来巨大的资源开销
网络爬虫的法律风险!!!
服务器上的数据有产权归属
网络爬虫获取数据后牟利将带来法律风险
网络爬虫泄露隐私
网络爬虫可能具备突破简单访问控制的能力,获得被保护数据从而泄露个人隐私
网络爬虫限制
**来源审查:**判断User-Agent进行限制域。检查来访的HTTP协议头的User-Agent域,只响应浏览器或友好的爬虫访问。
发布公告: Robots协议。告知所有爬虫网站的爬取策略,要求爬虫遵守。
Robots协议
Robots :Robots Exclusion Standard 网络爬虫排除标准
作用:网站告知网络爬虫哪些页面可以抓取,哪些不行。
形式:在网站的根目录下的robots.txt文件。写明哪些目录是允许爬取的,哪些不行
案例:京东的Robots协议
User-agent: *
Disallow: /?*
Disallow: /pop/*.html
Disallow: /pinpai/*.html?*
User-agent: EtaoSpider
Disallow: /
User-agent: HuihuiSpider
Disallow: /
User-agent: GwdangSpider
Disallow: /
User-agent: WochachaSpider
Disallow: /
Robots协议基本语法
# 注释
*代表所有
/代表根目录
User-agent:*
Disallow:/
国家教育部Robots协议,无robots协议
如果一个网站没有提供robots.txt,默认这个网站允许所有爬虫爬取其页面。
Robots协议的使用
网络爬虫:自动或人工识别robots.txt,再进行内容爬取。
约束性:Robots协议是建议但非约束性,网络爬虫可以不遵守,但存在法律风险。
对Robots协议的理解
爬取网页 玩转网页 | 爬取网站 爬取系列网站 | 爬取全网 |
---|---|---|
访问量很小:可以遵守 访问量较大:建议遵守 | 非商业且偶尔:建议遵守 商业利益:必须遵守 | 必须遵守 |
小结
Robots协议基本语法
网络爬虫,要亦有道
实例
一:京东商品信息爬取
import requests
url = 'https://item.jd.com/100008384344.html'
try:
r = requests.get(url)
r.raise_for_status()
r.encoding = r.apparent_encoding
print(r.text[:1000])
expect:
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])
expect:
print("爬取失败")
三、百度/360搜索关键词提交
百度
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()
r.encoding = r.apparent_encoding
print(len(r.text))
expect:
print("爬取失败")
360
import requests
keyword = 'Python'
try:
kv = {'q',keyword}
r = requests.get("http://www.so,com/s",params = kv)
print(r.request.url)
r.raise_for_status()
r.encoding = r.apparent_encoding
print(len(r.text))
expect:
print("爬取失败")
四、网络图片的爬取和存储
网络图片连接的格式
http://www.example.com/picture.jpg
国家地理网站
http://www.nationalgeographic.com.cn
选择一个图片web页面
http://www.ngchina.com.cn/photography/photo_of_the_day/3921.html
http://image.nationalgeographic.com.cn/2017/0211/20170211061910157.jpg
import requests
import os
url = "http://image.nationalgeographic.com.cn/2017/0211/20170211061910157.jpg"
root = "D://pics//"
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)
f.close()
print("文件保存成功")
else:
print("文件已存在")
expect:
print("爬取失败")
五、IP地址归属地的自动查询
IP138
import requests
url = "http://m.ip138.com/ip.asp?ip="
try:
r = requests.get(url + '202.204.80.112')
r.raise_for_status()
r.encoding = r.apparent_encoding
print(r.text[-500:])
expect:
print("爬取失败")
单元小结
五个实例:
Beautiful Soup 库的安装
安装 在cmd命令行中 输入
pip install beautifulsoup4
测试
演示HTML页面地址:http://python123.io/ws/demo.html
>>> import requests
>>> r = requests.get('http://python123.io/ws/demo.html')
>>> demo = r.text
>>> from bs4 import BeautifulSoup
>>> soup = BeautifulSoup(demo,'html.parser')
>>> print(soup.prettify())
BeautifulSoup 库的基本元素
BeautifulSoup库的理解
BeautifulSoup库是解析、遍历、维护“标签树”的功能库
<p>..<p><!--标签Tag-->
<p class='title'>...</p>
<!--p:名称name,成对出现;class 属性atttributes 0个或多个>
BeautifulSoup库,也叫beautifulsoup4或bs4。
最长引用方式
from bs4 import BeautifulSoup # 从bs4中引入一个BeautifulSoup类
import bs4
HTMLDocument<---------------------->标签树<------------------->BeautifulSoup类
例
>>> from bs4 import BeautifulSoup
>>> soup = BeautifulSoup("<html>data</html>","html.parser")
>>> soup = BeautifulSoup(open("D://demo.html"),"html.parser")
BeautifulSoup库解析器
解析器 | 使用方法 | 条件 |
---|---|---|
bs4的HTML解析器 | BeautifulSoup(mk,‘html.parser’) | 安装bs4库 |
lxml的HTML解析器 | BeautifulSoup(mk,‘lxml’) | pip install lxml |
lxml的XML解析器 | BeautifulSoup(mk,‘xml’) | pip install lxml |
html5lib的解析器 | BeautifulSoup(mk,‘html5lib’) | pip install html5lib |
BeatufulSoup类的基本元素
基本元素 | |
---|---|
Tag | 标签,最基本的信息组织单元,分别用<>和</>标明开头和结尾 |
Name | 标签的名字, … 的名字是‘p’,格式:.name |
Attributes | 标签的属性,字典形式组织,格式:.attrs |
NavigableString | 标签内非属性字段,<>…</>中字符串,格式:.string |
Comment | 标签内字符串的注释部分,一种特殊的Comment类型 |
基于bs4库的HTML内容遍历方法
下行遍历、上行遍历、平行遍历
标签树的下行遍历
属性 | 说明 |
---|---|
.content | 子节点的列表,将所有儿子节点存入列表 |
.children | 子节点的迭代类型,域content类似,用于循环遍历儿子节点 |
.descendants | 子孙节点的迭代类型,包含所有子孙节点,用于循环遍历 |
>>> soup = BeautifulSoup(demo,'html.parser')
>>> soup.head
>>> soup.head.contents
>>> soup.body.contents
标签树的上行遍历
属性 | 说明 |
---|---|
.parent | 节点的父亲标签 |
.parents | 节点先辈标签的迭代类型,用于循环遍历先辈节点 |
>>> soup.parent
>>> soup = BeautifulSoup(demo,'html.parser')
>>> for parent in soup.a.parents:
if parent is None:
print(parent)
else:
print(parent.name)
p
body
html
[document]
标签树的平行遍历
属性 | 说明 |
---|---|
.next_sibling | 返回按照HTML文本顺序的下一个平行节点标签 |
.previous_sibling | 返回按照HTML文本顺序的上一个平行节点标签 |
.next_siblings | 迭代类型,返回按照HTML文本顺序后续所有平行节点标签 |
.previous_siblings | 迭代类型,返回按照HTML文本顺序的前序所有平行节点标签 |
注意:平行遍历发生在同一个父节点下的个节点间
注意2:平行标签和儿子标签可能存在NavigableString类型的值(字符串)
for sibling in soup.a.next_siblings:
print(sibling)
for sinling in soup.a.previous_siblings:
print(sibling)
基于bs库的HTML格式输出
print(soup.prettify())在每一个标签后添加换行符
bs4库的编码:采用utf-8
>>> soup = BeautifulSoup("<p>中文</p>","html.parser")
>>> soup.p.string
'中文'
>>> print(soup.p.prettify())
<p>
中文
</p>
小结
BeautifulSoup库入门:基本元素:Tag Name Attributes NavigableString Comment
遍历功能:.content .children .descendants .parent .parents///next_sibling .previous_sibling .next_siblings .previsou_siblings
信息标记的三种形式
信息的标记
一个信息: ‘北京理工大学’
一组信息 ‘北京理工大学’、‘北京市海淀中关村’、1949
标记: name:‘北京理工大学’、address:‘北京市海淀中关村’
- 标记后的信息可形成信息组织结构,增加了信息维度
- 标记后的信息可用于通讯、存储或展示
- 标记的结构和信息一样具有重要价值
- 标记后的信息更利于程序理解和运用
HTML和其他的信息标记
HTML是WWW(World Wide Web)的信息组织方式。(声音、图像、视频等超文本)
HTML通过预定于的<>…</>标签形式组织不同类型的信息
信息标记主要有三种形式:xml、json、yaml
XML(eXtensible、Markup、Language)
<img src = "china.jpg" size="10">...</img>
<!--名字 属性 内容 名称-->
<<img src = "china.jpg" size="10"/>
<!--缩写形式-->
JSON (Javascript Object Notation)
有类型的键值对key:value,如
“name”:“北京理工大学”,
"year":1949
"names":["北京理工大学","延安自然科学院"]
"nameObject":{"newName":"北京理工大学","oldName":"延安自然科学院"}
Yaml(Aint’t Markup Language)
无类型键值对 name:北京理工大学
通过缩进形式表达所属关系,-表示并列关系,|表达整块数据,#表示注释
name:
-北京理工大学
-延安自然科学学院
text: |#学校介绍
石河子大学是国家“211工程”重点建设高校、国家西部重点建设高校和省部共建高校,是“中西部高校基础能力建设工程”和“中西部高校综合实力提升工程”(一省一校)入选高校。2017年学校入选国家“双一流”建设一流学科建设高校,2018年入选“部省合建”高校,纳入教育部直属高校排序。
三种信息标记比较
XML 最早的通用信息标记语言,可扩展性好,但繁琐。
JSON:信息有类型,适合程序处理(js),较XML简洁。
YAML:信息无类性,文本信息比例最高,可读性好
应用方向:
XML:Internet上的信息交互与传递。
JSON:移动应用云端和节点的信息通讯,无注释。
YAML:各类系统的配置文件,有注释,易读。
基于bs4库的HTML内容查找方法
<>.find_all(name,sttrs,recursive,string,**kwargs)
返回一个列表类型,存储查找的结果
name:对标签名称的检索字符串
attrs:对标签属性值的检索字符串,可标注属性检索
recursive:是否对子孙全部检索。默认Ture。
string:<>…</>中字符串区域的检索字符串。
简写形式:
- (…)等价于.find_all(…)
- soup(…)等价于soup.find_all(…)
扩展方法
方法 | 说明 |
---|---|
<>.find() | 搜索且只返回一个结果,字符串类型,同.find_all()参数 |
<>.find_parents() | 在先辈节点中搜索,返回列表类型,同.find_all()参数 |
<>.find_parent() | 在先辈节点中返回一个结果,字符串类型,同.find()参数 |
<>.find_next_siblings() | 后续平行节点中搜索,返回列表类型,同.find_all()参数 |
<>.find_next_sibling() | 后续平行节点中搜索返回一个结果,字符串类型,同.find_all()参数 |
<>.find_previous_siblings() | 在前序平行节点中搜索,返回列表类型,同.find_all()参数 |
<>.find_previous_sibling() | 在前序平行节点中搜索返回一个结果,字符串类型,同.find_all()参数 |
小结
信息标记与提取方法
XML、JSON、YAML
信息提取方法一般方法、等价形式和七个扩展方法
实例
实例一、“中国大学排名定向爬虫”实例介绍
功能描述:
输入:大学排名URL连接最好大学网
输出:大学排名信息的屏幕输出(排名、大学名称、总分)
技术路线:requests+bs4
定向爬虫:仅对输入url进行爬取,不扩展爬取
程序结构设计
步骤1:从网络上获取大学排名网页内容
getHTMLText()
步骤2:提取网页内容中信息到合适的数据结构
fillUnnivList()
步骤3:利用数据结构展示并输出结果
printUnivList()
import requests
from bs4 import BeautifulSoup
import bs4
def getHTMLText(url):
try:
r = requests.get(url, timeout=30)
r.raise_for_status()
r.encoding = r.apparent_encoding
return r.text
except:
return ""
return ""
def fillUnivList(ulist, html):
soup = BeautifulSoup(html)
for tr in soup.find("tbody").children:
if isinstance(tr, bs4.element.Tag):
tds = tr("td") # find_all()的简写形式
ulist.append([tds[0].string, tds[1].string, tds[3].string])
pass
def printUnivList(ulist, num):
print("{:^10}\t{:^26}\t{:^10}".format("排名", "学校名称", "总分"))
for i in range(num):
u = ulist[i]
print("{:^10}\t{:^26}\t{:^10}".format(u[0], u[1], u[2]))
print("Suc" + str(num))
def main():
uinfo = []
url = "http://www.zuihaodaxue.com/zuihaodaxuepaiming2016.html"
html = getHTMLText(url)
fillUnivList(uinfo, html)
printUnivList(uinfo, 20) # 20 univs
main()
优化:中文对齐问题的解决
采用中文字符的空格填充chr(12288)
正则表达式库Re入门
正则表达式的概念
regular expression regex RE
正则表达式是用来简洁表达一组字符串的表达式
如:‘PN’,‘PYN’,‘PYTN’,‘PYTHN’,‘PYTHON’,<======>正则表达式P(Y|YT|YTH|YTHO)?N
‘PY’,‘PYY’,‘PYYY’,…,‘PYYYY…’<=========>PY+
'PY’开头,后续存在不多于10个字符 ,后续字符不能是‘P’或‘Y’<===>PY[^PY]{0,10}
通用的字符串表达框架
用途:
- 表达文本类型的特征
- 同时查找或替换一组字符串
- 匹配字符串的全部或部分区域
编译:将符合正则表达式语法的字符串转换成增则表达式,编译后的结果与要表达的字符串(组)
正则表达式的语法
P(Y|YT|YTH|YTHO)?N:正则表达式由字符和操作符构成
正则表达式的常用的操作符
操作符 | 说明 | 实例 |
---|---|---|
. | 表示任何单个字符 | |
[] | 字符集,对单个字符给出取值范围 | [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次(含n) | ab{1,2}表示abc、abbc |
^ | 匹配字符串开头 | ^abc表示abc且在一个字符串的开头 |
$ | 匹配字符串结尾 | 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’、‘PYTHONNNN’… |
PY[TH]ON | ‘PYTON’、‘PYHON’ |
PY[^TH]?ON | ‘PYON’、‘PYaON’、‘PYbON’、‘PYCON’、‘PYdON’、… |
PY{:3}n | ‘PN’、‘PYN’、‘PYYN’、‘PYYYN’ |
经典的正则表达式
正则表达式 | 意义 |
---|---|
1+$ | 由26个字母组成的字符串 |
2+$ | 由26个字母和数字组成的字符串 |
^-?\d+$ | 整数形式的字符串 |
3*[0-9][0-9]*$ | 正整数形式的字符串 |
[1-9]\d{5} | 中国境内邮政编码,6位 |
[\u4e00-\u9fa5] | 匹配中文字符 |
\d{3}-\d{8}|\d{4}-\d{7} | 国内电话号码,010-68913536 |
案例
匹配IP地址的正则表达式
IP地址字符串形式的正则表达式(IP地址分4端,每段0-255)
Re库的基本使用
介绍:Re库是Python的标准库,主要用于字符串匹配。
调用方式
import Re
表示类型:
-
raw string类型(原生字符串类型)
如:Re库采用raw string 类型表示正则表达式,表示为r’text’
原生字符串和字符串所不同的是只需要在字符串前加一个小写的r
raw string 是不包含转义符的字符串
-
string类型,更繁琐
建议当正则表达式中有转义字符的时候,使用raw string类型
主要功能函数
函数 | 说明 |
---|---|
re.search() | 在一个字符串中搜索匹配正则表达式的第一个位置,返回match对象 |
re.match() | 从一个字符串的开始位置起匹配正则表达式,返回match对象 |
re.findall() | 搜索字符串,以列表类型返回全部能匹配的字符串 |
re.split() | 将一个字符串按照正则表达式匹配结果进行分割,返回列表类型 |
re.finditer() | 搜索字符串,返回一个匹配结果的迭代类型,每一个迭代元素是match对象 |
re.sub() | 在一个字符串中替换所有匹配正则表达式的字符串,返回替换后的字符串 |
re.search(pattern,string,flags)
在一个字符串中搜索匹配正则表达式的第一个位置,返回match对象。
- pattern:正则表达式的字符串或原生字符串表示
- string:待匹配字符串
- flags:正则表达式使用时的控制标记
常用flags标记主要有
常用标记 | 说明 |
---|---|
re.I re.IGNORECASE | 忽略正则表达式的大小写,[A-Z]能够匹配小写字符 |
re.M re.MULTILINE | 正则表达式中的^操作符能够将给定字符串的每行当作匹配开始 |
re.S re.DOTALL | 正则表达式中的.操作符能够匹配所有字符,默认匹配除换行外的所有字符 |
>>> import re
>>> match = re.search(r'[1-9]\d{5}','BIT110081')
>>> if match::
print(match.group(0))
110081
>>>
re.match(pattern,string,flags)
从一个字符串的开始位置起匹配正则表达式,返回match对象
- pattern:正则表达式的字符串或原生字符串表示
- string:待匹配字符串
- flags:正则表达式使用时的控制标记
re.findall(pattern,string,flags)
搜索字符串,以列表类型返回全部能匹配的字符串
- pattern:正则表达式的字符串或原生字符串表示
- string:待匹配字符串
- flags:正则表达式使用时的控制标记
re.split(pattern,string,maxsplit,flags)
将一个字符串按照正则表达式匹配结果进行分割,返回列表类型
- pattern:正则表达式的字符串或原生字符串表示
- string:待匹配字符串
- maxsplit:最大分割数,剩余部分最为一个整体输出
- flags:正则表达式使用时的控制标记
re.finditer(pattern,string,flags)
搜索字符串,返回一个匹配结果的迭代类型,每一个迭代元素是match对象
- pattern:正则表达式的字符串或原生字符串表示
- string:待匹配字符串
- flags:正则表达式使用时的控制标记
re.sub(pattern,repl,string,count=0,flags=0)
在一个字符串中替换所有匹配正则表达式的字符串,返回替换后的字符串
- pattern:正则表达式的字符串或原生字符串表示
- repl:替换匹配字符串的字符串
- string:待匹配字符串
- count:匹配的最大替换次数
- flags:正则表达式使用时的控制标记
Re库的另一种等价用法
>>>rst = re.search(r'[1-9]\d{5}','BIT 100081') # 函数式用法:一次性操作
'''面向对象用法:编译后的多次操作'''
>>>pat = re.compiler(r'[1-9\d{5}]')
>>>rst = pat.search(BIT 100081)
regex = re.compile(pattern,flags = 0)
将正则表达式的字符串形式编译成正则表达式对象
- pattern:正则表达式的字符串或原生字符串表示
- flags:正则表达式使用时的控制标记
等价方法对应
函数 | 说明 |
---|---|
regex.search() | 在一个字符串中搜索匹配正则表达式的第一个位置,返回match对象 |
regex.match() | 从一个字符串的开始位置起匹配正则表达式,返回match对象 |
regex.findall() | 搜索字符串,以列表类型返回全部能匹配的字符串 |
regex.split() | 将一个字符串按照正则表达式匹配结果进行分割,返回列表类型 |
regex.finditer() | 搜索字符串,返回一个匹配结果的迭代类型,每一个迭代元素是match对象 |
regex.sub() | 在一个字符串中替换所有匹配正则表达式的字符串,返回替换后的字符串 |
注:参数中不再需要正则表达式字符串
match对象
match对象的属性
属性 | 说明 |
---|---|
.string | 待匹配的文本 |
.re | 匹配时使用的pattern对象(正则表达式对象) |
.pos | 正则表达式搜索文本的开始位置 |
.endpos | 正则表达式搜索文本的结束位置 |
match对象的常用方法
方法 | 说明 |
---|---|
.group(0) | 获得匹配后的字符串 |
.start() | 匹配字符串在原始字符串的开始位置 |
.end() | 匹配字符串在原始字符串的结束位置 |
.span() | 返回(.start(),.end()) |
Re库的贪婪匹配和最小匹配
实例
>>> match = re.search(r'PY.*N','PYANBNCNDN')
>>> match.group(0)
'PYANBNCNDN'
贪婪匹配:
Re库默认采用贪婪匹配,即输出匹配最长的子串。
最小匹配:
如何输出最短的字符串呢?
>>> match = re.search(r'PY.*?N','PYANBNCNDN')
>>> match.group(0)
'PYAN'
最小匹配操作符
操作符 | 说明 |
---|---|
*? | 前一个字符0次或无限次扩展,最小匹配 |
+? | 前一个字符1次或无限次扩展,最小匹配 |
?? | 前一个字符0次或1次扩展,最小匹配 |
{m,n}? | 扩展前一个字符m至n次(含n),最小匹配 |
实例
淘宝商品比价定向爬虫实例介绍
功能描述:
- 目标:获取淘宝搜索页面的信息,提取其中的商品名称和价格。
- 理解:
- 淘宝搜索接口
- 翻页的处理
- 技术路线:requests-re
程序的结构设计
- 步骤1:提交商品搜索请求,循环获取页面。
- 步骤2:对于每一个页面,提取商品名称和价格信息。
- 步骤3:将信息输出到屏幕上。
import requests
import re
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 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])
title = eval(tlt[i].split(':')[1])
ilt.append([price, title])
except:
print("解析异常")
print("")
def printGoodsList(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]))
print("")
def main():
goods = '书包'
depth = 2
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
printGoodsList(infoList)
main()
当前淘宝网需要先登录才能搜索
Scrapy网络爬虫框架
scrapy安装
执行:
pip install scrapy
scrapy不是一个简单的python库,是一个网络爬虫框架
爬虫框架定义
爬虫框架是实现爬虫功能的一个软件结构和功能组集合。
爬虫框架是一个半成品,能够帮助用户实现专业网络爬虫
Scrapy爬虫框架结构
"5+3"结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-39lEyfeT-1669043941495)(C:\Users\admin\Desktop\scrapy.PNG)]
其中 ENGINE、SCHEDULER、DOWNLOADER是已有实现
SPIDERS、ITEM PIPELINES需要用户编写(配置)
Scrapy爬虫框架解析
Engine 模块(不需要用户修改)
- 控制所有模块之间的数据流
- 根据条件触发事件
Downloader根据请求下载网页(不需要用户修改)
Scheduler 对所有爬虫爬取请求进行调度管理(不需要用户修改)
DownLoader Middleware
-
目的:实施Engine、Scheduler和DownLoader之间进行用户可配置的控制
-
功能:修改、丢弃、新增请求或响应
Spider(需要用户编写代码)
- 解析Downloader返回的响应(Response)
- 产生爬取网页(scraped item)
- 产生额外的爬取请求(Request)
Item Pipelines
- 以流水线方式处理Spider产生的爬取项(需要用户编写代码)
- 由一组操作顺序组成,类似流水线,每一个操作时ItemPipeline类型
- 可能操作包括:清理、检验、和查重爬取项的HTML数据、将数据存储到数据库。
Spider Middleware(用户可以编写配置代码)
- 目的:对请求和爬取项的载处理
- 修改、丢弃、新增请求或爬取项
requests库和scrapy对比
相同点:
两者都可以进行页面请求和爬取,Python爬虫的两个技术路线
两者可用性都好,文档丰富,入门简单
两者都没有处理js、表单提交、验证码等功能(可扩展)。
不同点
requests | Scrapy |
---|---|
页面级爬虫 | 网站级爬虫 |
功能库 | 框架 |
并发行考录不足,性能较差 | 并发性好,性能较高 |
尊重点在于页面下载 | 重点在于爬虫结构 |
定制灵活 | 一眼定制灵活,深度定制困难 |
上手十分简单 | 入门稍难 |
选用那个技术路线开发爬虫
- 非常小的需求,request库。
- 不太小的需求,Scrapy框架
- 定制程度很高的需求(不考虑规模),自搭框架,request>Scrapy
Scrapy常用命令
Scrapy命令行
scrapy命令行是为持续运行设计的专业爬虫框架,提供操作的Scrap命令行。
Scrapy命令行格式
> scrapy [options][args]
命令 | 说明 | 格式 |
---|---|---|
startproject | 创建一个新工程 | scrapy startprojrct <name> [dir] |
genspider | 创建一个爬虫 | scrapy genspider [options]<name><domain> |
settings | 获得一个爬虫 | scrapy settings[options] |
crawl | 运行一个爬虫 | scrapy crawl<spider> |
list | 列出工程中所有爬虫 | scrapy list |
shell | 启动URL调试命令行 | scrpy shell[url] |
Scrapy爬虫的命令行逻辑
为什么Scrapy采用命令行创建和运行爬虫
- 命令行(不是图形界面)更容易自动化,适合脚本控制
- 本质山,Scrapy是给程序员使用的,功能(而不是界面)更重要。
实例
演示HTML地址页面爬取
产生步骤:
-
步骤1:建议一个Scrapy爬虫工程
scrapy startproject python123demo
生成的工程文件目录结构
─python123demo ------>外层目录 │ scrapy.cfg ------>部署Scrapy爬虫的配置文件 │ └─python123demo ------>Scrapy框架的用户自定义Python代码 │ items.py ---->Items代码模板(继承类) │ middlewares.py ----->Middlewares代码模板(继承类) │ pipelines.py ----->Pipelines代码模板(继承类) │ settings.py ----->Scrapy怕成的配置文件 │ __init__.py ---->初始化脚本 │ ├─spiders ----->Spiders代码模板目录(继承类) │ │ __init__.py ---->初始文件,虚无修改 │ │ │ └─__pycache__ ----->缓存目录,无需修改 └─__pycache__
-
步骤2:在工程中产生Scrapy爬虫
scrapy genspider demo python123.io
在spiders目录下多出一个demo.py文件
# -*- coding: utf-8 -*-
import scrapy
class DemoSpider(scrapy.Spider):
name = 'demo'
allowed_domains = ['python123.io']
start_urls = ['http://python123.io/']
def parse(self, response):
pass
parse用于处理响应,解析内容形成字典,发现新的URL爬取请求
-
步骤3:配置产生的spider爬虫
# -*- coding: utf-8 -*- import scrapy class DemoSpider(scrapy.Spider): name = 'demo' # allowed_domains = ['python123.io'] start_urls = ['http://python123.io/ws/demo.html'] def parse(self, response): fname = response.url.split('/')[-1] with open (fname,'wb') as f: f.write(response.body) self.log("Saved file %s." %name)
完整版
# -*- coding: utf-8 -*- import scrapy class DemoSpider(scrapy.Spider): name = 'demo' def start_request(self): urls = ['http://python123.io/ws/demo123.html'] for url in urls: yield scrapy.Request(url=url, callback=self.parse) def parse(self, response): fname = response.url.split('/')[-1] with open (fname,'wb') as f: f.write(response.body) self.log("Saved file %s." %name)
-
步骤4:运行爬虫,获取网页
yield关键字
yield<------------------>生成器
- 生成器是一个不断产生值的函数
- 包含yielde语句的函数是一个生成器
- 生成器每次产生一个值(yield语句),函数被冻结,被唤醒后再产生一个值
生成器实例
# 生成器写法
>>> def gen(n):
for i in range(n):
yield i**2
>>> for i in gen(5):
print(i," ",end="")
0 1 4 9 16
# 普通写法
>>> def square(n):
ls = [i**2 for i in range(n)]
return ls
>>> for i in square(5):
print(i," ",end="")
为何要有生成器?
生成器相比一次列车出所有内容的优势
- 更节省存储空间
- 响应更迅速
- 使用更加灵活
Scrapy爬虫的基本使用
Scrapy爬虫的使用步骤
步骤1:创建一个工程和Spider模板
步骤2:编写spider
步骤3:编写Item Pipline
步骤4:优化配置策略
涉及三各类:
Request类、Response类、Item类
Request类
class scrapy.http.Request()
- Request对象表示一个Http请求。
- 由Spider生成,由Downloader执行
六个常用的属性和方法
属性或方法 | 说明 |
---|---|
.url | Request对应请求的URL地址 |
.method | 对应请求的方法,‘GET’、‘POST’等 |
.headers | 字典类型风格的请求头 |
.body | 请求内容主体,字符串类型 |
.meta | 用户添加的扩展信息,在Scrapy内部模块建传递信息使用 |
.copy() | 复制该请求 |
Response类
class scrapy.http.Response()
-
Response兑现表示一个HTTP响应。
-
由Downloader生成,由Spider处理
七个常用的属性或方法
属性或方法 | 说明 |
---|---|
.url | Response对应的URL地址 |
.status | HTTP状态吗,默认200 |
.headers | Response对应的头部信息 |
.body | Response对应的内容信息,字符串类型 |
.flags | 一组标记 |
.request | 产生Response类型对应的Resquest对象 |
.copy() | 复制该响应 |
Item类
class scrapy.item.Item()
- Item对象表示一个从HTML页面中提取的内容信息
- 由Spider生成,由Item PipeLine处理
- Item类似字典类型,可以按照字典类型操作。
Scrapy爬虫提取信息的方法
scrapy爬虫支持使用多种HTML信息提取方法
- Beautiful Soup
- lxml
- re
- XPath Selector
- CSS Selector
CSS Selector基本使用
.css('a::attr(href)').extract()CSS Selector由W3C组织维护并规范
为何要有生成器?
生成器相比一次列车出所有内容的优势
- 更节省存储空间
- 响应更迅速
- 使用更加灵活
## Scrapy爬虫的基本使用
### Scrapy爬虫的使用步骤
步骤1:创建一个工程和Spider模板
步骤2:编写spider
步骤3:编写Item Pipline
步骤4:优化配置策略
### 涉及三各类:
Request类、Response类、Item类
#### Request类
class scrapy.http.Request()
- Request对象表示一个Http请求。
- 由Spider生成,由Downloader执行
六个常用的属性和方法
| 属性或方法 | 说明 |
| ---------- | -------------------------------------------------- |
| .url | Request对应请求的URL地址 |
| .method | 对应请求的方法,‘GET’、‘POST’等 |
| .headers | 字典类型风格的请求头 |
| .body | 请求内容主体,字符串类型 |
| .meta | 用户添加的扩展信息,在Scrapy内部模块建传递信息使用 |
| .copy() | 复制该请求 |
#### Response类
class scrapy.http.Response()
- Response兑现表示一个HTTP响应。
- 由Downloader生成,由Spider处理
七个常用的属性或方法
| 属性或方法 | 说明 |
| ---------- | ---------------------------------- |
| .url | Response对应的URL地址 |
| .status | HTTP状态吗,默认200 |
| .headers | Response对应的头部信息 |
| .body | Response对应的内容信息,字符串类型 |
| .flags | 一组标记 |
| .request | 产生Response类型对应的Resquest对象 |
| .copy() | 复制该响应 |
#### Item类
class scrapy.item.Item()
- Item对象表示一个从HTML页面中提取的内容信息
- 由Spider生成,由Item PipeLine处理
- Item类似字典类型,可以按照字典类型操作。
### Scrapy爬虫提取信息的方法
scrapy爬虫支持使用多种HTML信息提取方法
- Beautiful Soup
- lxml
- re
- XPath Selector
- CSS Selector
#### CSS Selector基本使用
<HTML>.css('a::attr(href)').extract()
CSS Selector由W3C组织维护并规范