Python爬虫从入门到放弃

python爬虫从入门到放弃之五:Requests库高级用法

2019-07-18  本文已影响7人  52d19f475fe5

——前面讲过,HTTP Requests包含请求方式、请求URL、请求头、请求体(即携带的参数),如果我们爬虫请求URL失败,那很有可能是我们的Requests数据与浏览器的不一样。所以还是那句话,浏览器做什么,爬虫就做什么!

Requests库13个控制访问参数详解:

如下图,是爬"有道翻译"的请求头


笨办法是把所有参数带上,在逐个尝试删除

而实际上,只有Host、Referer、User-Agent、Cookie 是有用的,但不是绝对的

Cookie虽然包含在headers中,但Requests库有单独参数来处理它,headers={}内就不要放它了

范例代码:

import requests

r = requests.get('https://www.zhihu.com/')
print(r.status_code)


headers = {'User-Agent':'Mozilla/5.0'}
r = requests.get('https://www.zhihu.com/',headers = headers)
print(r.status_code)

运行结果:

400
200
>>> 

上面的代码中,我们对知乎发起两次请求,不带请求头返回400,可见知乎在headers做了来源审查
把上面的代码修改一下,分别出打印两次访问的请求头

范例代码:

import requests

r = requests.get('https://www.zhihu.com/')
print(r.request.headers)
print('')

headers = {'User-Agent':'Mozilla/5.0'}
r = requests.get('https://www.zhihu.com/',headers = headers)
print(r.request.headers)

运行结果:

{'User-Agent': 'python-requests/2.22.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive'}

{'User-Agent': 'Mozilla/5.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Cookie': 'tgw_l7_route=a37704a413efa26cf3f23813004f1a3b; _zap=94c457cd-7c53-4f60-b463-4f8c024dbe06; _xsrf=3ppe1f3Dh00KARJ3rybWippiTSM2LBFo'}
>>> 

我们可以看到,'User-Agent'字段不同,第一次请求是'User-Agent': 'python-requests/2.22.0',很明显,我们第一次请求的代码在告诉知乎:我是一个python爬虫

范例代码:

import requests

payload = {'key1': 'value1', 'key2': 'value2'}
r = requests.get("http://httpbin.org/get", params=payload)
print(r.url)

运行结果:

http://httpbin.org/get?key1=value1&key2=value2
>>> 
# 百度的关键词提交接口
http://www.baidu.com/s?wd=keyword

# 360的关键字词接口
http://www.so.com/s?q=keyword

keyword表示关键字,我们用params={'wd':keywoed}传入url,实现百度搜索,如下代码:

import requests

kv = {'wd':'python'}
r = requests.get('http://www.baidu.com/s',params = kv)
print(r.url)

post()传参Json数据

import requests
import json

url = 'http://httpbin.org/post'
payload = {'some': 'data'}
r = requests.post(url, data=json.dumps(payload))
print(r.text)

运行结果:

{
  "args": {}, 
  "data": "{\"some\": \"data\"}", 
  "files": {}, 
  "form": {}, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Content-Length": "16", 
    "Host": "httpbin.org", 
    "User-Agent": "python-requests/2.22.0"
  }, 
  "json": {
    "some": "data"
  }, 
  "origin": "119.129.129.70, 119.129.129.70", 
  "url": "https://httpbin.org/post"
}

>>> 

我们看到"some": "data"被放到了Json字段下,json.dumps()是字典/列表转Json的方法

post()传参files文件
如下,在本地新建一个report.xls文件,写上"Hello world"


运行如下代码:
import requests

url = 'http://httpbin.org/post'
files = {'file': open('report.xls', 'rb')}
r = requests.post(url, files=files)
print(r.text)

运行结果:

{
  "args": {}, 
  "data": "", 
  "files": {
    "file": "Hello World\r\n"
  }, 
  "form": {}, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Content-Length": "159", 
    "Content-Type": "multipart/form-data; boundary=f40f0efd2466dba3e629ac1d905e7d04", 
    "Host": "httpbin.org", 
    "User-Agent": "python-requests/2.22.0"
  }, 
  "json": null, 
  "origin": "119.129.130.109, 119.129.130.109", 
  "url": "https://httpbin.org/post"
}

>>> 

我们看到,"Hello world"被放到了 files字段下

import requests

with open('massive-body','rb') as f:
    requests.post('http://some.url/streamed', data=f)

Response headers,我们可以看到set cookies参数。set cookies是什么意思?就是服务器往浏览器写入了cookies。如下图:

Response headers
cookies基本展示:
import requests

r = requests.get("https://www.baidu.com/")
print(r.cookies)
print(type(r.cookies))
print('')
r = requests.get("https://www.so.com/")
print(r.cookies)
print(type(r.cookies))

运行结果:

<RequestsCookieJar[<Cookie BDORZ=27315 for .baidu.com/>]>
<class 'requests.cookies.RequestsCookieJar'>

<RequestsCookieJar[<Cookie QiHooGUID=266A3B3FBBF19BFE07FB2135103F842F.1563469171236 for www.so.com/>, <Cookie _S=2j7n8h174crroeq5a8npi3lm87 for www.so.com/>]>
<class 'requests.cookies.RequestsCookieJar'>
>>> 

我们看到,r.cookies返回的是CookieJar对象,仔细看,在Cookiefor字段是键值对,如:BDORZ=27315,也就是说,它的行为跟字典相似,那有没有方法可以让它跟字典互换呢?答案是有的

import requests

r = requests.get("https://www.baidu.com/")

# cookies、dict互换
cookies_dict = requests.utils.dict_from_cookiejar(r.cookies)
print(type(cookies_dict),'——',cookies_dict)

cookies = requests.utils.cookiejar_from_dict(cookies_dict)
print(type(cookies),'——',cookies)

# 以下方法也可以实现cookies转dict
cookie_dict = r.cookies.get_dict()
print(type(cookie_dict),'——',cookie_dict)

运行结果:

<class 'dict'> —— {'BDORZ': '27315'}
<class 'requests.cookies.RequestsCookieJar'> —— <RequestsCookieJar[<Cookie BDORZ=27315 for />]>
<class 'dict'> —— {'BDORZ': '27315'}
>>> 



如果,你想发送你的cookies到服务器,也可以直接使用 cookies 的字典dict形式

import requests

r = requests.get(url,cookies = cookies_dict)
r = requests.post(url,data = payload,cookies = cookies_dict)

——以上的请求中,每次请求其实都相当于发起了一个新的请求。也就是相当于我们每个请求都用了不同的浏览器单独打开的效果。也就是它并不是指的一个会话,即使请求的是同一个网址。比如

import requests

requests.get('http://httpbin.org/cookies/set/sessioncookie/123456789')
r = requests.get("http://httpbin.org/cookies")
print(r.text)

运行结果:

{
  "cookies": {}
}

>>> 

很明显,这不在一个会话中,无法获取 cookies,那么在一些站点中,我们需要保持一个持久的会话,就像用一个浏览器逛淘宝一样,在不同的选项卡之间跳转,这样其实就是建立了一个长久会话。

import requests

s = requests.Session()
s.get('http://httpbin.org/cookies/set/sessioncookie/123456789')
r = s.get("http://httpbin.org/cookies")
print(r.text)

运行结果:

{
  "cookies": {
    "sessioncookie": "123456789"
  }
}

>>> 

会话中,我们请求了两次,一次是设置 cookies,一次是获得 cookies

我们发现可以成功获取到 cookies 了,这就是建立一个会话起到作用

范例代码:

import requests,json

session = requests.Session()
headers = {'User-Agent': 'Mozilla/5.0'}
    
def cookies_read():
    f = open('cookies.txt', 'r')
    cookies_json = f.read()
    f.close()
    cookies_dict = json.loads(cookies_json)
    cookies = requests.utils.cookiejar_from_dict(cookies_dict)
    return cookies
 
def sign_in():
    url = ' XXXXX'
    data = {'XXXXX'}
    session.post(url,headers=headers,data=data)
    cookies_dict = requests.utils.dict_from_cookiejar(session.cookies)
    cookies_json = json.dumps(cookies_dict)
    f = open('cookies.txt', 'w')
    f.write(cookies_json)
    f.close()

def run():
    url = 'XXXXX'
    data = {'XXXXX'}
    return (session.post(url, headers=headers, data=data))

try:
    session.cookies = cookies_read()
except FileNotFoundError:
    sign_in()
    session.cookies = cookies_read()

q = run()
if q.status_code == 200:
    print('成功啦!')
else:
    sign_in()
    session.cookies = cookies_read()
    q = run()

以上,替换你的url、data或headers试试

默认情况下,除了 HEAD, Requests 会自动处理所有重定向。

import requests

r = requests.get('http://github.com')
print(r.url)
print(r.status_code)
print(r.history)
print(type(r.history))

运行结果:

https://github.com/
200
[<Response [301]>]
<class 'list'>
>>> 

可以看到,我们的URL由HTTP 请求重定向到 HTTPS

如果,想禁用重定向,可以这么做:

import requests

r = requests.get('http://github.com',allow_redirects=False)
print(r.url)
print(r.status_code)
print(r.history)

运行结果:

http://github.com/
301
[]
>>> 
import requests

try:
    requests.get('http://github.com', timeout=0.001)
except requests.exceptions.Timeout:
    print('0.001秒内未响应,产生超时!')

运行结果:

0.001秒内未响应,产生超时!
>>> 

timeout 仅对连接过程有效,与响应体的下载无关。 timeout 并不是整个下载响应的时间限制,而是如果服务器在 timeout 秒内没有应答,将会引发一个异常!

遇到网络问题(如:DNS 查询失败、拒绝连接等)时,Requests 会抛出一个 ConnectionError 异常

如果 HTTP 请求返回了不成功的状态码, Response.raise_for_status() 会抛出一个 HTTPError异常

若请求超时,则抛出一个 Timeout 异常

若请求超过了设定的最大重定向次数,则会抛出一个 TooManyRedirects 异常

所有Requests显式抛出的异常都继承自 requests.exceptions.RequestException

import requests

def run():
    try:
        r = requests.get('http://github.com', timeout = 20)
        r.raise_for_status()
        r.encoding = r.apparent_encoding
        return r.text
    except requests.exceptions.RequestException:
        print('产生异常')
        
run()

范例代码:

import requests

url = "https://inv-veri.chinatax.gov.cn/"
headers = {"User-Agent":"Mozilla/5.0"}

r = requests.get(url,headers = headers)
r.encoding = r.apparent_encoding
print(r.status_code)
print(r.text[:500])

运行结果:


SSLError

加上一个参数:verify=证书路径,或verify=False

import requests

url = "https://inv-veri.chinatax.gov.cn/"
headers = {"User-Agent":"Mozilla/5.0"}

r = requests.get(url,headers = headers,verify=False)
r.encoding = r.apparent_encoding
print(r.status_code)
print(r.text[:500])

运行结果:

返回200,有警告
在urllib3官方文档找到处理warning的方法
禁用SSL警告

通过urllib3设置禁用警告的方式来屏蔽这个警告:

import requests
import urllib3

urllib3.disable_warnings()
url = "https://inv-veri.chinatax.gov.cn/"
headers = {"User-Agent":"Mozilla/5.0"}

r = requests.get(url,headers = headers,verify=False)
r.encoding = r.apparent_encoding
print(r.status_code)
print(r.text[:500])

通过捕获警告到日志的方式忽略警告:

import requests
import logging

logging.captureWarnings(True)
url = "https://inv-veri.chinatax.gov.cn/"
headers = {"User-Agent":"Mozilla/5.0"}

r = requests.get(url,headers = headers,verify=False)
r.encoding = r.apparent_encoding
print(r.status_code)
print(r.text[:500])

运行结果:

看起来很美丽

问题:为什么爬虫要使用代理?

根据协议类型,选择不同的代理

import requests

proxies= {
    "http":"http://127.0.0.1:9999",
    "https":"http://127.0.0.1:8888"
}
r = requests.get("http://www.baidu.com",proxies = proxies)
print(r)

proxies= {}内放一个就可以了

你可以使用IP池做随机数切换IP,否则使用单一IP意义不大

如果代理需要设置账户名和密码,只需要将字典更改为如下:
proxies = {
"http":"http://user:password@127.0.0.1:9999"
}

如果你的代理是通过sokces这种方式则需要pip install "requests[socks]"
proxies= {
"http":"socks5://127.0.0.1:9999",
"https":"sockes5://127.0.0.1:8888"
}

import requests

r = requests.get("http://120.27.34.24:9001/",auth=("user","123"))
print(r.status_code)
import requests

r = requests.get('http://httpbin.org/get')

print(r.status_code)
print(r.encoding)
print(r.apparent_encoding)
print(r.headers)
print(r.request.headers)
print(r.text)
print(r.content)
print(r.url)
print(r.cookies)
print(r.history)



>>>阅读更多文章请点击以下链接:

python爬虫从入门到放弃之一:认识爬虫
python爬虫从入门到放弃之二:HTML基础
python爬虫从入门到放弃之三:爬虫的基本流程
python爬虫从入门到放弃之四:Requests库基础
python爬虫从入门到放弃之五:Requests库高级用法
python爬虫从入门到放弃之六:BeautifulSoup库
python爬虫从入门到放弃之七:正则表达式
python爬虫从入门到放弃之八:Xpath
python爬虫从入门到放弃之九:Json解析
python爬虫从入门到放弃之十:selenium库
python爬虫从入门到放弃之十一:定时发送邮件
python爬虫从入门到放弃之十二:多协程
python爬虫从入门到放弃之十三:Scrapy概念和流程
python爬虫从入门到放弃之十四:Scrapy入门使用

上一篇 下一篇

猜你喜欢

热点阅读