在anaconda中安装requests库,因为我的python环境是anaconda搭建的虚拟环境。
只需要在anaconda Prompt中输入以下代码即可:
conda install requests
安装成功后可以在cmd中试验一下。在使用pycharm调用requests库时,我需要配置其解释器,前几次碰到过这样的问题,明明用anaconda安装了模块,但是在pycharm中无法调用,后来发现就是解释器没有配置好。
在setting>Project:pyfile>Project Interpreter中配置,如下图:
我的是选中anaconda中安装python的位置,确认即可。
开始愉快的爬虫之旅吧!
方法 | 说明 |
---|---|
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 |
request方法:requests.request(method, url, **kwargs)
method:请求方式,对应get/put/post等7种方法,即HTTP的那些功能。
url:拟获取页面的url链接
**kwargs:13个控制访问的参数,均为可选项
1)params:字典或字节序列,作为参数增加到url中;
import requests
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
通过这个方法,可以把一些键值对增加到url中,使得url再次访问时,不止访问的是这个资源,而同时带入了这些参数,而服务器根据这些参数,筛选部分资源返回回来。
2)data:字典、字节序列或文件对象,作为Request的内容;重点是作为向服务器提供或提交资源时使用。提交的资源并不放在url链接里,而是放在url链接对应位置的地方,作为数据来存储。
import requests
kv = {'key1': 'value1', 'key2': 'value2'}
r = requests.request('POST', 'http://python123.io/ws', data=kv)
3)json:JSON格式的数据,作为Request的内容
也是作为内容部分可以向服务器提交。
4)headers:字典,HTTP定制开头;它对应了向某一个url访问时所发起的HTTP头字段,也就是说我们可以用这个字段来定制访问某一个url的HTTP的协议头。
我们可以定义一个字典hd = {'user-agent': 'Chrome/10'}
来修改HTTP协议中的user-agent字段,我们把user-agent变为Chrome/10,
r = requests.request('POST', 'http://python123.io/ws', headers=hd)
那么在访问某一个链接时,我们可以把这样的字段赋给headers,此时headers再去向服务器访问时,服务器看到的user-agent字段就是Chrome/10,也即Chrome浏览器的第十个版本;这种模拟浏览器的方法就是在header字段中实现。
4)cookies:指的是字典或CookieJar,Request中的cookie,从HTTP中解析cookie
auth:是元组类型,支持HTTP认证功能
5)files:字典类型,向服务器传输文件时使用的字段
import requests
fs = {'file': open('data.xls', 'rb')}
r = requests.request('POST', 'http://python123.io/ws', files=fs)
以file和对应文件为键值对,对应到相关的url上。
6)timeout:设定超时时间,秒为单位
可设置一个timeout时间,如果在timeout时间内,我们的请求内容没有返回回来,那么将产生一个timeout的异常。
r = requests.request('GET', 'http://www.baidu.com', timeout=10)
7)proxies字段:字典类型,设定访问代理服务器,可以增加登录认证。
如下,使用两个代理,一个是http访问时使用的代理,代理中可以增加用户名跟密码的设置,我们再增加一个https的代理服务器,我们再访问百度时所使用的IP地址就是代理服务器的IP地址,使用这个字段可以有效的隐藏用户爬取网页的源IP地址的信息,能够有效地防止对爬虫的逆追踪。
import requests
pxs = {'http': 'http://user:[email protected]:1234',
'https': 'https:10.10.10.1:4321'}
r = requests.request('GET','http://www.baidu.com', proxies=pxs)
8)allow_redirects:True/False,默认为True,重定向开关;此开关表示允不允许对url重定向。
stream:True/False,默认为True,获取内容立即下载开关。
verify字段:True/False,默认为True,认证SSL证书开关。
sert:是保存本地SSL证书路径的字段
最简单的应用方法即r=requests.get(url)
,构造一个向服务器请求资源的Request对象,返回一个包含服务器资源的Response对象,用r来表示返回的所有相关资源。
完整使用方法包含三个参数:requests.get(url, params=None, **kwargs)
url:拟获取页面的url链接
params:url中的额外参数,字典或字节流格式,可选
**kwargs:12个控制访问的参数
理解Response的编码:有的header中有charset,即表示有编码要求,那就,但并不是所有的网址都能返回这个值,所以就会默认编码为ISO-8859-1,但这一编码不能解析中文。当我们用encoding不能正确返回内容时,就要用apparent_encoding来从内容中分析出编码方式。
属性 | 说明 |
---|---|
r.encoding | 从HTTP header中猜测的响应内容编码方式 |
r.apparent_encoding | 从内容中分析出的响应内容编码方式 (备选编码方式) |
可以通过以下代码试验:
import requests
r = requests.get('http://www.baidu.com')
# 查看状态码
print(r.status_code)
print(r.text)
print(r.encoding)
print(r.apparent_encoding)
# 改变编码方式,并输出
r.encoding = 'utf-8'
print(r.text)
通常使用get()函数,最大的作用是使得用户访问网页变得更有效更稳定更可靠。
因为网络连接有风险,因此异常处理很重要,Requests库常用的六种连接异常,最后一种是触发生成:
异常 | 说明 |
---|---|
requests.ConnectionError | 网络连接错误异常,如DNS查询失败、拒绝连接等 |
requests.HTTPError | HTTP错误异常 |
requests.URLRequired | URL缺失异常 |
requests.ToolManyRedirects | 超过最大重定向次数,产生重定向异常 |
requests.ConnectTimeout | 连接远程服务器超时异常 |
requests.Timeout | 请求URL超时,产生超时异常 |
r.raise_for_status() | 如果不是200,产生异常requests.HTTPError |
代码框架如下(如果状态不是200,引发HTTPError异常,判断网络连接的正常):
def getHTMLText():
try:
r = requests.get(url, timeout=30)
r.raise_for_status()
r.encoding = r.apparent_encoding
return r.text
except:
return '产生异常'
if __name__ == '__main__':
url = 'http://www.baidu.com'
print(getHTMLText())
HTTP,Hypertext Transfer Protocol,超文本传输协议,是一个基于“请求与响应”模式的、无状态的应用层协议。即用户发起请求,服务器做出响应,无状态指第一次请求和第二次请求之间没有相关的关联,应用层协议指的是该协议工作在TCP协议之上。
HTTP协议采用URL作为定位网络资源的标识。
URL格式如下:http://host[:port][path]
两个反斜杠之后有三个域,host表示合法的Internet主机域名或IP地址;port表示端口号,缺省端口为80;path是请求资源的路径。URL是通过HTTP协议存取资源的Internet路径,一个URL对应一个数据资源。
HTTP协议对资源的操作
方法 | 说明 |
---|---|
GET | 请求获取URL位置的资源 |
HEAD | 请求获取URL位置资源的响应消息报告,即获得该资源的头部信息 |
POST | 请求向URL位置的资源后附加新的数据 |
PUT | 请求向URL位置寸尺一个资源,覆盖原URL位置的资源 |
PATCH | 请求局部更新URL位置的资源,即改变该处资源的部分内容 |
DELETE | 请求删除URL位置存储的资源 |
用户通过GET或者HEAD获取全部信息或者头部信息,如果想将自己的资源放在URL对应的位置上就要用PUT、POST、PATCH方法。当面对庞大资源时,采用PATCH可以只修改需要的,可以节省带宽。
使用head()方法
requests.head(url,**kwargs)
**kwargs:13g个访问控制参数
使用post()及其他方法
向URL POST一个字典,自动编码为form(表单),而字符串会默认归为到data下。
put()方法与post()方法类似。
requests.post(url, data=None, json=None,**kwargs)
requests.put(url, data=None, **kwargs)
requests.patch(url, data=None, **kwargs)
requests.delete(url, **kwargs)
Robots Exclusion Standard 网络爬虫排除标准
作用:网站告知网络爬虫哪些页面可以抓取,哪些不行。
形式:在网站根目录下的robots.txt文件。
在域名后,输入robots.txt,如https://www.taobao.com/robots.txt
Robots协议基本语法:
User-agent:*
Disallow:/
*代表所有,/代表根目录
如何遵守呢,网络爬虫应该可以自动或人工识别robots.txt,再进行内容爬取,爬虫爬的好,局子进的早,还是好好遵守规则。当然如果访问量小,不涉及商业利益,不违法还是可以不遵守的。
import requests
url1 = 'https://item.taobao.com/item.htm?id=638296572648'
url2 = 'https://item.jd.com/100023800830.html'
try:
kv = {'user-agent': 'Mozilla/5.0'}
r = requests.get(url2, headers=kv)
# 查看状态码
print(r.status_code)
# 查看编码
print(r.encoding)
print(r.request.headers)
r.raise_for_status()
print(r.text[:10000])
except:
print('爬取失败')
百度的关键词接口:
https://www.baidu.com/s?wd=keyword
360关键词的接口:
https://so.com/s?q=keyword
好家伙,百度搜索会有安全验证,然后我试了试bing搜索
import requests
url = 'https://cn.bing.com/search'
kv1 = {'q': 'python'}
try:
kv = {'user-agent': 'Mozilla/5.0'}
r = requests.get(url, headers=kv, params=kv1)
r.encoding = r.apparent_encoding
r.raise_for_status()
print(r.text[:20000])
except:
print('爬取失败')
然后干脆试了一下京东搜索物品,还可以,虽然不知道参数为什么两个才行,一个就不对,显示需要登陆。
import requests
url1 = 'https://search.jd.com/Search'
kv1 = {'wq': '男装', 'keyword': '男装'}
try:
kv = {'user-agent': 'Mozilla/5.0'}
r = requests.get(url1, headers=kv, params=kv1)
r.raise_for_status()
print(r.text[:20000])
except:
print('爬取失败')
这个还好,就要弄清楚填什么关键字段,就比如其他的IP查询网址就就可能不是这么提交的。
import requests
url1 = 'https://m.ip138.com/iplookup.asp?ip='
try:
kv = {'user-agent': 'Mozilla/5.0'}
r = requests.get(url1+'202.204.80.112', headers=kv)
r.encoding = r.apparent_encoding
r.raise_for_status()
print(r.text[:20000])
except:
print('爬取失败')
安装还是用的conda install beautifulsoup4
但是需要注意的是调用的时候,from bs4 import BeautifulSoup
使用一个html网页来演示,获得HTML的源代码
获取源代码的方式:
import requests
from bs4 import BeautifulSoup
url1 = 'http://python123.io/ws/demo.html'
try:
kv = {'user-agent': 'Mozilla/5.0'}
r = requests.get(url1, headers=kv)
demo = r.text
# 给出demo,同时给出解释器,此处为HTML的解释器
soup = BeautifulSoup(demo, 'html.parser')
print(soup.prettify())
r.raise_for_status()
print(r.text[:20000])
except:
print('爬取失败')
因此使用bs4只需要两行
from bs4 import BeautifulSoup
soup = BeautifulSoup('data
', 'html.parser')
第一个参数是我们需要解析的一个html格式的信息,第二个是解析器。
Beautiful Soup类的基本元素
基本元素 | 说明 |
---|---|
Tag | 标签,最基本的信息组织单元,分别用<>和>标明开头和结尾 |
Name | 标签的名字, …的名字是’p’,格式: |
Attributes | 标签的属性,字典形式组织,格式: |
NavigableString | 标签内非属性字符串,<>…>中字符串,格式: |
Comment | 标签内字符串的注释部分,一种特殊的Comment类型 |
获取标题标签内容soup.title
获取链接标签soup.a
,这样只能获取第一个标签内容
获取标签名字的方法soup.a.name
还可以查看该标签的父亲是soup.a.parent.name
还可以继续往上查看,使用.name
的方式获取名字,使用.parent.name
查看父类,以字符串类型输出
查看标签的属性信息,标签的属性是在标签中标明标签特点的相关区域,以字典形式组织,给出了属性名和属性的键值对,可以使用字典的方式对每一个属性的提取,soup.a.attrs
查看标签类型,type(soup.a)
,看标签类型是如何定义的:
。
tag标签可以有0个或多个属性,当没有属性的时候使用.attrs
获得的字典是空字典,但是无论有无属性都可以获得字典。
获取Tag标签的NavigableString属性,也就是两个尖括号之间的内容,使用soup.a.string
,其类型为
再html中使用来表示注释,输出注释部分可以用.string
来输出,查看其类型来确认。
需要对HTML基本格式有了解。
了解了基本格式,就可以了解几种遍历方式:
标签数的下行遍历一共包含三个属性
属性 | 说明 |
---|---|
.contents | 子节点的列表,将 |
.children | 子节点的迭代类型,与.contents类似,用于循环遍历儿子节点 |
.descendants | 子孙节点的迭代类型,包含所有子孙节点,用于循环遍历 |
对于标签的儿子节点不仅仅包括标签节点,也包括字符串节点,比如'\n'
遍历方式:
for child in soup.body.children:
print(child)# 遍历儿子节点
for child in soup.body.descendants:
print(child)# 遍历子孙节点
上行遍历的两个属性
属性 | 说明 |
---|---|
.parent | 节点的父亲标签 |
.parents | 节点先辈标签的迭代类型,用于循环遍历先辈节点 |
标签树的上行遍历代码
soup = BeautifulSoup(demo, 'html.parser')
for parent in soup.a.parents:
if parent is None:
print(parent)
else:
print(parent.name)
在遍历一个标签的所有先辈标签时,会遍历到soup本身,而soup的先辈并不存在.name的信息,所以我们需要做出一个区分,如果先辈是none,我们就不能打印这方面的信息。
beautifulsoup4库一共提供了4个平行遍历属性:
属性 | 说明 |
---|---|
.next_sibling | 返回按照HTML文本顺序的下一个平行节点标签 |
.previous_sibling | 返回按照HTML文本顺序的上一个平行节点标签 |
.next_siblings | 迭代类型,返回按照HTML文本顺序的后续所有平行节点标签 |
.previous_siblings | 迭代类型,返回按照HTML文本顺序的前续所有平行节点标签 |
标签数的平行遍历是有条件的,所有的平行遍历必须发生在同一个父亲节点下,如果不是同一个父亲节点下的标签之间不构成平行遍历关系。在我们的数据结构中,title和p标签就不是,而body下的两个p标签就是平行标签。
因为存在NavigableString元素中字符串也算为节点,依次标签对应的平行节点不一定是标签。
平行遍历方式:
for sibling in soup.a.next_siblings:
print(sibling)# 遍历后续节点
for sibling in soup.a.previous_siblings:
print(sibling)# 遍历前续节点
prettify
方法,能使每一个标签及内容分行显示。能对soup处理,也能对每个标签处理。soup.prettify()
或者soup.a.prettify()
。信息标记的一般种类:
XML、JSON和YAML
...
或者
内容为空时可采用一对尖括号即可,也可插入注释内容
键: 值
,多值时采用键: [值1, 值2]
,键值对可以嵌套使用,此时用{,}的形式来体现。一般用在程序对接口处理的地方,能作为程序代码的一部分,并被程序直接运行,缺陷就是无法体现注释。1)完整解析信息的标记形式,再提取关键信息
需要标记解析器,如之前解析html的解析器,使用bs4的标签树来遍历。
2)无视标记形式,直接搜索关键信息(文本查找函数,如find_all()方法)
3)最好当然是结合,结合形式解析和搜索方法,提取关键信息。
例:提取html中所有URL链接
思路:第一步:搜索到所有的标签
第二步,用标签的解析格式,提取href属性,获得链接内容
from bs4 import BeautifulSoup
url1 = 'http://python123.io/ws/demo.html'
try:
kv = {'user-agent': 'Mozilla/5.0'}
r = requests.get(url1, headers=kv)
demo = r.text
soup = BeautifulSoup(demo, 'html.parser')
for link in soup.find_all('a'):
print(link.get('href'))
r.raise_for_status()
except:
print('爬取失败')
1)
bs4库提供了一个方法来查找<>.find_all(name, attrs, recursive, string, **kwargs)
,这个方法可以在soup的遍历中取查找里面的信息,返回一个列表类型,存储查找的结果。
soup.find_all('a')
,输出一个包含所有a标签的列表;如果查找两个标签,那么可以使用soup.find_all(['a', 'b'])
,以列表形式作为第一个参数传递。如果我们给的标签名称是true,将显示当前soup的所有标签信息。soup.find_all('p', 'course')
来检查p标签中是否包含course字符串,返回一个列表,里边给出了带有course属性值的p标签;也可以直接对属性做相关约定,例:查找ID属性等于link1的值作为查找元素,soup.find_all(id='link1')
,如果没有此标签,那么会返回一个空列表。soup.find_all(string = re.compile('python'))
2)
由于find_all()函数很常见,所以在bs4库中有简写:
等价于
,对soup变量也如此,将标签改为soup即可。
3)还有find_all()方法的扩展方法
方法 | 说明 |
---|---|
<>.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()参数 |
使用上海交大的排名网站查询:https://www.shanghairanking.cn/rankings/bcur/2021 1)因为教程内容是多年前的,所以有些东西需要改变,就比如将td标签内容传递入列表中需要改变方法,教程中是使用 1)上边的解决大学名称的输出 函数 2)对齐问题 输出中文字符,中文字符宽度不够时,采用西文字符填充;中西文字符占用宽度不同。
输入:大学排名URL链接
输出:大学排名信息的屏幕输出(排名、大学名称和总分)
技术路线:requests-bs4
定向爬虫:仅对输入URL进行爬取,不扩展爬取。
程序设计:
步骤1:从网络上获取大学排名网页内容,定义函数get_html_text()
步骤2:提取网页内容中信息到合适的数据结构,定义函数text_to_list()
步骤3:利用数据结构展示并输出结果univ_list_print()
观察源代码我们发现,排名信息都存于这个表格标签中,在其中,每一个大学的所有信息又被封装在
<\tr>中,每一个信息又被 <\td>所包含。所以需要先找到tbody标签,然后再tbody标签中解析tr标签,再把tr标签中的td标签找到,把相关信息找到放入列表中。
3.2 代码编写
#!/usr/bin/python
import requests
from bs4 import BeautifulSoup
import bs4
def get_html_text(url):
try:
r = requests.get(url, timeout=30)
r.raise_for_status()
r.encoding = r.apparent_encoding
return r.text
except:
print('爬取失败')
return ''
def text_to_list(univ_info, html):
soup = BeautifulSoup(html, 'html.parser')
# 查找tbody标签,并将它的孩子做一个遍历
for tr in soup.find('tbody').children:
# 排除掉其他的节点非标签类的其他信息,比如字符串
if isinstance(tr, bs4.element.Tag):
# 获取其中td标签内容
tds = tr('td')
univ_info.append([tds[0].text.strip(), tds[1].text.strip(), tds[4].text.strip()])
def univ_list_print(univ_info, num):
print('{:^10}\t{:^16}\t{:^10}'.format('排名', '学校名称', '总分'))
for i in range(num):
u = univ_info[i]
print('{:^10}\t{:^16}\t{:^14}'.format(u[0], u[1][:8], u[2]))
def main():
univ_info = []
url = 'https://www.shanghairanking.cn/rankings/bcur/2021'
html = get_html_text(url)
text_to_list(univ_info, html)
univ_list_print(univ_info, 20) # 20所学校
if __name__ == '__main__':
main()
.string
但是因为网址的改变,会将style="display:none"
也传入列表,会有报错:TypeError: unsupported format string passed to NoneType.__format__
去网上找了解决方法:问题解决:TypeError: unsupported format string passed to NoneType.format
强哇!CSDN永远滴神!
2)刚开始发现报错是AttributeError: 'NoneType' object has no attribute 'children'
,是的,好多错,一个一个来,我看了下网上说的,他们是在第一个函数的时候其实就是运行错误了,但是第一个函数用了try-except来解决异常问题,所以返回空列表,造成后续的问题,没有子孙节点。他们是timeout等关键词写错了,我看了看发现是在最开始自己瞎调试的一些代码没删掉,哈哈哈哈这个问题解决了,我也是看到说传递的是空列表才反应过来,所以我在except异常处理里面加了语句:print('爬取失败')
。
3)输出大学名称的时候,我发现输出了很多字符串,有大学名称后边跟着大学英文名和大学标签,后来查看源代码,发现大学名称只是在<\a>标签中;所以有解决办法一:这个办法看起来正统一些,将a标签的内容单独导出来,传入列表。第二个方法就是,我在看输出的东西的时候,直接截取前边字符串。笑哭,我要为自己的机智折服。
4)输出的表格还是会有参差不齐,主要是因为有些大学名字太长 了,对于强迫症来说有点难受,解决办法:来源还是上边的老哥整出来的,这我是真不会。 tplt = "{0:^10}\t{1:{3}^12}\t{2:^10}"
# 0、1、2为槽,{3}表示若宽度不够,使用format的3号位置处的chr(12288)(中文空格)进行填充
print(tplt.format("排名","学校名称","总分",chr(12288)))
3.3 优化实现
def text_to_list(univ_info, html):
该函数内容修改如下afile = tr('a')
tds = tr('td')
univ_info.append([tds[0].text.strip(), afile[0].string, tds[4].text.strip()])
def univ_list_print(univ_info, num):
修改def univ_list_print(univ_info, num):
print('{:^10}\t{:^16}\t{:^10}'.format('排名', '学校名称', '总分'))
for i in range(num):
u = univ_info[i]
print('{:^10}\t{:^16}\t{:^14}'.format(u[0], u[1], u[2]))
format方法中相关的约定
:
<填充>
<对齐>
<宽度>
,
<.精度>
<类型>
引导符号
用于填充的单个字符
<左对齐>右对齐^居中对齐
槽的设定输出宽度
数字的千位分隔符适用于整数和浮点数
浮点数小鼠部分的精度或字符串的最大输出长度
整数类型b c d o x X浮点数类型e E f %
优化1:
中文字符宽度不够,采用中文字符来填补
嗷!采用中文字符的空格填充chr(12288)(这个就是utf-8编码对应中文字符的空格)
排版下来就很舒服(用左对齐以及字符宽度来调就是下图这样了)
你可能感兴趣的:(爬虫初阶,python初阶,python,爬虫,request)