python爬虫系列之 requests: 让 HTTP 服务人类

一、安装requests库

pip install requests

二、简单的请求

requests支持所有的HTTP请求,以最常用的get方法为例:

import requests


url = 'https://www.baidu.com'
response = requests.get(url)

比想象中要简单的多吧,只要把要访问的网址当作参数传递给requests.get方法,就可以获得所请求的网页。

请求成功后会返回一个Response对象,这个对象包含了服务器对请求的响应和对响应的一些操作。

三、响应的操作

  • text属性和content属性

通过text属性可以查看响应的内容

#注:这里的response是前面代码块中的response,
#本系列文章所有代码都是前后关联的
print(response.text)

text属性通常是网页的源代码,也就是在浏览器里查看源码后看到的代码。如果请求的是一个js文件的话,text就是js文件的内容,css文件同理。总之,text属性是响应内容的文本形式。

当返回的响应是二进制的内容时(比如图片或视频),text属性通常不能正常使用,这时可以使用content属性来保存响应内容。下面的代码请求了一张图片并保存到当前目录下:

img_url = 'https://www.cnblogs.com/skins/CodingLife/images/title-yellow.png'
r = requests.get(url)
with open('img.png', 'wb+') as f:
    f.write(r.content)
  • status_code属性

status_code属性是响应的状态码,它表示了请求的状态,200表示请求成功,301和302表示请求重定向,具体描述请看下方表格。

状态码 描述
1** 信息,服务器收到请求,需要请求者继续执行操作
2** 成功,操作被成功接收并处理
3** 重定向,需要进一步的操作以完成请求
4** 客户端错误,请求包含语法错误或无法完成请求
5** 服务器错误,服务器在处理请求的过程中发生了错误

需要注意的是,很多情况下收到5**状态码并不是服务器发生了错误,而是服务器拒绝了请求(在浏览器里遇到5**状态码的话那服务器很大可能是真的出错了),出现这种情况一般是因为服务器有反爬措施。

想了解更多关于http状态码的问题可以点击文末的链接

  • apparent_encoding 和encoding属性

apparent_encoding属性是所请求的网页的编码,在网页中用下面的标签表示:

<meta charset="UTF-8">

有时候requests自动使用的编码不能编码网页就可以使用apparent_encoding和 encoding两个属性来设置编码。类似下面的代码:

r.encoding = r.apparent_encoding 

这时候可能有人会想到,是不是每次请求的时候都使用这个方法就可以解决网页乱码了呢?

不是的!这种方法尽量只在网页可能会出现乱码的时候使用,每次请求都使用的话反而会造成乱码。

尤其是针对一些年代比较久远的网站,比如政府机关、学校或者公益组织的网站。这些网站由于技术的原因网页的编码很可能和网页上标识的编码不一样,使用上面的技巧反而弄巧成拙。

四、设置请求头

前面讲了一些使用requests的基础知识,下面我们进入正题,讲讲怎么设置请求头、如何发送带参数的请求、get请求和post请求的区别。

对于一般的网站来说请求头可以说是反爬的核心手段了,所以对于每个学习爬虫的人都有必要了解一下请求头。既然说到请求头我们就来看一个真正的请求头吧。

GET https://www.jianshu.com/users/472a595d244c/courses?page=1&count=10 HTTP/1.1
Host: www.jianshu.com
Connection: keep-alive
Accept: application/json
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36
Referer: https://www.jianshu.com/u/472a595d244c
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: ***********************
If-None-Match: W/"************************"

对于所有的请求头来说它们的第一行的格式都是相同的:

GET https://www.jianshu.com/users/472a595d244c/courses?page=1&count=10 HTTP/1.1
请求方式 请求的网址 使用的HTTP协议版本

请求方式一共分为六种,其中最常用的是GET和POST请求,对于爬虫来说了解了这两种请求也就足够了,其他的可以先放到一边。

第二行到最后一行是请求头字段,其中需要了解的是Host、User-Agent、Referer和Cookie。

Host字段表示请求网站的主域名,也就是请求的网址中的www.jianshu.com部分,Referer表示请求发起页面的地址,一般的反爬机制就是通过识别Host和Rederer字段来进行反爬。

User-Agent是浏览器标识头,requests默认不设置这个字段,而且大部分的请求库或者爬虫框架这个字段都是很有特点的(这里的有特点指的是和浏览器的差别很大),许多的网站根据这个字段来对爬虫做初步的判断。

也有少量的网站是通过 IP来鉴别爬虫的,同一个 IP访问频率过快就会遭到封禁,这时还想继续爬就得用代理。

不过一般的服务器还是通过headers来区别爬虫与人的,所以我们只要伪造一个headers就能以假乱真,这样我们又可以开心地爬了<( ̄︶ ̄)↗[GO!]

这时候可能有人会问,怎么伪造headers呢?

有一个简单粗暴的方法就是直接用chrome的开发者模式把网站的请求头复制下来,不过对付一般的网站我们只需要user-agent就行(有些甚至不需要 headers)。

伪造好 headers后,headers传递给 get方法的 headers参数就可以用伪造的 headers来访问网站了

#简单粗暴的方法
headers = {
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
    'Accept-Encoding': 'gzip, deflate',
    'Accept-Language': 'zh-CN,zh;q=0.9',
    'Cache-Control': 'max-age=0',
    'Content-Type': 'application/x-www-form-urlencoded',
    'Host': 'www.baidu.com',
    'Proxy-Connection': 'keep-alive',
    'Upgrade-Insecure-Requests': '1',
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.117 Safari/537.36',
}
#一般的方法
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.117 Safari/537.36',
}

r = requests.get(url, headers=headers)
  • 带 query string的请求

    现讲到这里,我们已经可以请求一些简单的网站了,然而问题并没有那么简单。

    当你在浏览网页的时候,你可能会发现有的 url竟然长这样:

    http://zhannei.baidu.com/cse/search?q=%B9%F3%D7%E5%CE%C6%D5%C2&s=13049992925692302651&entry=1

    (上面的链接是一个小说网站的站内搜索链接,我顺手拿过来用了)

    百度一下,我们知道了 “?” 后面跟着的是查询字符串(query string)

    服务器会根据查询字符串来返回不同的结果

    这个时候聪明的你一定会想到我们可以控制 query来获得不同的响应

    一个初级的版本长这样:

    q = '贵族纹章'
    s = '13049992925692302651'
    entry = '1'
    url = 'http://zhannei.baidu.com/cse/search' + '?' + 'q=' + q + '&s=' + s + '&entry=' + entry
    

    但是这样的代码可读性实在是差,看得脑壳痛<( _ _ )>

    下面给出一个更简洁的写法:

    url = 'http://zhannei.baidu.com/cse/search?q={}&s={}&entry={}'.format(q, s, entry)
    

    这样已经很简洁了,但是仔细分析一下,我们发现返回的结果是由 q、s、entry三个变量共同决定的。

    也就是说 q、s、entry三个变量是有某种联系的,所以我们需要把这三个变量看作一个对象

    这时候最好的方法是,用一个字典表示它们(刚好 requests有 params参数来接收一个 dict作为 query string):

    params = {
          'q': '贵族纹章',
      's': '13049992925692302651',
      'entry': '1',
    }
    url = 'http://zhannei.baidu.com/cse/search'
    r = requests.get(url, params=params)
    
  • post 方法提交表单

    现在我们就可以轻松地爬很多网页了,但是上面讲到的方法只适用于用 get 方法访问的网页。

    如果碰到要登录或者需要提交表单的网页就束手无策了,遇到这种情况 post 方法就排上用场了。

    post 方法的使用和 get 方法一样,也可以传入 headers 参数。

    唯一不同的就是 post 方法用 data 来接收要提交的表单数据,data 要求是字典格式 data 的键对应 html 中表单的 name,举例如下:

    示例网页:

    <!DOCTYPE html>
    <html>
    <head>
        <title>注册</title>
        <meta charset="utf-8">
    </head>
    <body>
           <form action="#" method="post">
                <div class="row">
                    <span class="col s4">username:</span><input type="text" name="username">
                </div>
                <div class="row">
                    <span class="col s4">password:</span><input type="password" name="password">
                </div>
                <div class="row">
                    <button class="col s4 center" type="submit">提交</button>
                </div>
            </form>
    </body>
    </html>
    
    #这个网页可以返回 post方法提交的表单数据
    url = 'http://203.195.204.124/post'
    
    #构造 data 字典 username 、password 分别和上方网页中的 username 和 password 对应
    data = {
        'username': 'geebos',
        'password': '123456',
    }
    
    r = requests.post(url, data=data)
    #也可以传入 headers 参数
    r = requests.post(url, data=data, headers=headers)
    print(r.text)
    '''返回的内容(r.text):{"username": "geebos", "password": "123456"}''
    

    当然,真正的 post 请求比这个例子要复杂一些,至少密码是经过加密再进行传输的

五、高级用法 session会话

在一些需要登录的网站中我们需要跨请求保持 cookie,但是前面所讲的 post和 get方法不会保存 cookie。

虽然我们可以通过提取 cookie给 headers参数来保持 cookie,但这种方法有个缺点就是必须要手动提取 cookie,要是碰到上百个登录... ...

幸好 requests库的 Session类可以跨请求保持 cookie,省去了我们的体力劳动。

session的使用很简单,下面通过几行代码来演示 session的使用:

#获得一个 session实例 想要跨请求保持 cookie必须在同一个 session下调用方法
session = requests.Session()

url = 'https://www.baidu.com'

#get方法
r = session.get(url)
#post方法
r = session.post(url, data=data)

六、总结

  • get请求

    url

    params

    headers

  • post请求

    url

    params

    headers

    data

  • session会话

    要跨请求保持 cookie必须在同一个 session实例下调用方法

有问题欢迎评论

http状态码:HTTP状态吗|菜鸟教程

下一篇:python爬虫系列之 requests实战:用 requests库下载网页和图片

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,504评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,434评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,089评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,378评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,472评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,506评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,519评论 3 413
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,292评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,738评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,022评论 2 329
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,194评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,873评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,536评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,162评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,413评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,075评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,080评论 2 352

推荐阅读更多精彩内容