Python网络爬虫(五)- Requests和Beautiful Soup

目录:

1.简介

Requests 是用Python语言编写,基于 urllib,采用 Apache2 Licensed 开源协议的 HTTP 库。它比 urllib 更加方便,可以节约我们大量的工作,完全满足 HTTP 测试需求。

  • Beautiful is better than ugly.(美丽优于丑陋)
  • Explicit is better than implicit.(清楚优于含糊)
  • Simple is better than complex.(简单优于复杂)
  • Complex is better than complicated.(复杂优于繁琐)
  • Readability counts.(重要的是可读性)

2.安装

  • 利用 pip 安装
pip install requests
  • 或者利用 easy_install
easy_install requests

3.基本请求方式

requests为爬虫开发者们提供了8种基本的请求方式,由于在web开发过程中,大家发现对于服务器数据的处理方式没有一开始制定标准时设计的那么复杂,所以一般情况下都简化成了get/post两种常见的请求方式

req = requests.request(method,url, **kw)
req = requests.post(url, **kw)
req = requests.get(url, **kw)
req = requests.delete(url, **kw)
req = requests.put(url, **kw)
req = requests.head(url, **kw)
req = requests.options(url, **kw)
req = requests.patch(url, **kw)

1.基本GET请求

  • 最基本的GET请求可以直接用get方法
#python开发人员常用的测试地址 http://httpbin.org/gets
r = requests.get("http://httpbin.org/get")
  • 如果想要加参数,可以利用 params 参数
import requests
payload = {'key1': 'value1', 'key2': 'value2'}
r = requests.get("http://httpbin.org/get", params=payload)
print r.url

运行结果:

http://httpbin.org/get?key2=value2&key1=value1
  • 如果想请求JSON文件,可以利用 json() 方法解析

写一个JSON文件命名为a.json

["foo", "bar", {
 "foo": "bar"
}]

利用如下程序请求并解析

import requests
r = requests.get("a.json")
print r.text
print r.json()

运行结果如下,其中一个是直接输出内容,另外一个方法是利用 json() >方法解析

["foo", "bar", {
"foo": "bar"
}]
[u'foo', u'bar', {u'foo': u'bar'}]
  • 如果想获取来自服务器的原始套接字响应,可以取得 r.raw 。 不过需要>在初始请求中设置 stream=True 。
r = requests.get('https://github.com/timeline.json', stream=True)
r.raw
<requests.packages.urllib3.response.HTTPResponse object at >0x101194810>
r.raw.read(10)
'\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\x03'

这样就获取了网页原始套接字内容。

  • 如果想添加 headers,可以传 headers 参数
import requests
payload = {'key1': 'value1', 'key2': 'value2'}
headers = {'content-type': 'application/json'}
r = requests.get("http://httpbin.org/get", params=payload, headers=headers)
print r.url

2.基本POST请求

  • 对于 POST 请求来说,我们一般需要为它增加一些参数。那么最基本的传参方法可以利用 data 这个参数。
import requests
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.9.1"
 }, 
 "json": null, 
 "url": "http://httpbin.org/post"
}
  • 有时候我们需要传送的信息不是表单形式的,需要我们传JSON格式的数据过去,所以我们可以用 json.dumps() 方法把表单数据序列化。
import json
import requests
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.9.1"
 }, 
 "json": {
   "some": "data"
 },  
 "url": "http://httpbin.org/post"
}

通过上述方法,我们可以POST JSON格式的数据

  • 上传文件,那么直接用 file 参数即可

新建一个 a.txt 的文件,内容写上 Hello World!

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

运行结果:

{
 "args": {}, 
 "data": "", 
 "files": {
   "file": "Hello World!"
 }, 
 "form": {}, 
 "headers": {
   "Accept": "*/*", 
   "Accept-Encoding": "gzip, deflate", 
   "Content-Length": "156", 
   "Content-Type": "multipart/form-data; >boundary=7d8eb5ff99a04c11bb3e862ce78d7000", 
   "Host": "httpbin.org", 
   "User-Agent": "python-requests/2.9.1"
 }, 
 "json": null, 
 "url": "http://httpbin.org/post"
}

这样我们便成功完成了一个文件的上传。

  • requests 是支持流式上传的,这允许你发送大的数据流或文件而无需先把它们读入内存。要使用流式上传,仅需为你的请求体提供一个类文件对象即可
with open('massive-body') as f:
    response = requests.post('http://some.url/streamed', data=f)
    print(response.text)

4.Cookies

  • 如果一个响应中包含了cookie,那么我们可以利用 cookies 变量来拿到
import requests

response = requests.get("http://www.baidu.com/")

# 返回CookieJar对象:
cookiejar = response.cookies

#将CookieJar转为字典:
cookiedict = requests.utils.dict_from_cookiejar(cookiejar)

print cookiejar

print cookiedict

5.超时配置

  • 用 timeout 变量来配置最大请求时间
requests.get('http://github.com', timeout=0.001)

注:timeout 仅对连接过程有效,与响应体的下载无关。

6.会话对象

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

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,一次是获得 cookies
运行结果

{
 "cookies": {
   "sessioncookie": "123456789"
 }
}
  • 发现可以成功获取到 cookies 了,这就是建立一个会话到作用。
    那么既然会话是一个全局的变量,那么我们肯定可以用来全局的配置了。
import requests

s = requests.Session()
s.headers.update({'x-test': 'true'})
r = s.get('http://httpbin.org/headers', headers={'x-test2': 'true'})
print r.text

通过 s.headers.update 方法设置了 headers 的变量。两个变量都传送过>去了。
运行结果:

{
 "headers": {
   "Accept": "*/*", 
   "Accept-Encoding": "gzip, deflate", 
   "Host": "httpbin.org", 
   "User-Agent": "python-requests/2.9.1", 
   "X-Test": "true", 
   "X-Test2": "true"
 }
}
  • get方法传的headers 同样也是 x-test,它会覆盖掉全局的配置
{
 "headers": {
   "Accept": "*/*", 
   "Accept-Encoding": "gzip, deflate", 
  "Host": "httpbin.org", 
   "User-Agent": "python-requests/2.9.1", 
   "X-Test": "true"
 }
}
  • 如果不想要全局配置中的一个变量设置为 None 即可
r = s.get('http://httpbin.org/headers', headers={'x-test': None})

运行结果:

{
 "headers": {
  "Accept": "*/*", 
   "Accept-Encoding": "gzip, deflate", 
   "Host": "httpbin.org", 
   "User-Agent": "python-requests/2.9.1"
 }
}

7.SSL证书验证

现在随处可见 https 开头的网站,Requests可以为HTTPS请求验证SSL证书,就像web浏览器一样。要想检查某个主机的SSL证书,你可以使用 verify 参数,比如12306的证书无效。

测试如下:

import requests
r = requests.get('https://kyfw.12306.cn/otn/', verify=True)
print r.text

结果:

requests.exceptions.SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:590)
测试12306的证书无效
  • 如果我们想跳过刚才 12306 的证书验证,把 verify 设置为 False 即可
import requests

r = requests.get('https://kyfw.12306.cn/otn/', verify=False)
print r.text

在默认情况下 verify 是 True,所以如果需要的话,需要手动设置下这个变量。
源码中我们可以看到默认verify=False
  • 也可以引入python 关于证书的处理模块SSL
# 1. 导入Python SSL处理模块
import ssl

# 2. 表示忽略未经核实的SSL证书认证
context = ssl._create_unverified_context()

8.代理

  • 如果需要使用代理,你可以通过为任意请求方法提供 proxies 参数来配置单个请求
import requests

# 根据协议类型,选择不同的代理
proxies = {
 "https": "http://41.118.132.69:4433"
 "http": "http://41.118.132.69:4433"
}
r = requests.post("http://httpbin.org/post", proxies=proxies)
print r.text
  • 私密代理验证(特定格式) 和 Web客户端验证(auth 参数)
#私密代理
import requests

# 如果代理需要使用HTTP Basic Auth,可以使用下面这种格式:
proxy = { "http": "mr_mao_hacker:sffqry9r@61.158.163.130:16816" }

response = requests.get("http://www.baidu.com", proxies = proxy)

print response.text
  • web客户端验证
    如果是Web客户端验证,需要添加 auth = (账户名, 密码)
import requests

auth=('test', '123456')

response = requests.get('http://192.168.199.107', auth = auth)

print response.text
  • 也可以通过环境变量 HTTP_PROXY 和 HTTPS_PROXY 来配置代理
export HTTP_PROXY="http://10.10.1.10:3128"
export HTTPS_PROXY="http://10.10.1.10:1080"

通过以上方式,可以方便地设置代理。

5.程序中的使用

安装好request模块之后,在程序中就可以通过import引入并且使用了

  • 代码操作(一)爬取天气
# -*- coding:utf-8 -*-
# 引入requests模块
import requests

# 发送请求,得到服务器返回的响应对象,通过encoding设置响应中数据的编码
response = requests.get("http://www.sojson.com/open/api/weather/json.shtml?city=%E9%83%91%E5%B7%9E")
response.encoding="utf-8"

# 通过text打印响应中的文本数据
print(response.text)

结果:

{"message":"Success !",
"status":200,"city":"郑州","count":26,
"data":{"shidu":"74%","pm25":71.0,"pm10":145.0,"quality":"良","wendu":"31",
"ganmao":"极少数敏感人群应减少户外活动",
"yesterday":{"date":"10日星期四","sunrise":"05:41","high":"高温 37.0℃",
"low":"低温 25.0℃","sunset":"19:20","aqi":141.0,"fx":"西风","fl":"<3
级","type":"晴","notice":"lovely sunshine,尽情享受阳光的温暖吧"},
"forecast":[{"date":"11日星期五","sunrise":"05:42","high":"高温 37.0℃",
"low":"低温 25.0℃","sunset":"19:19","aqi":100.0,"fx":"南风",
"fl":"<3级","type":"晴","notice":"天气干燥,请适当增加室内湿度"},
{"date":"12日星期六","sunrise":"05:42","high":"高温 31.0℃","low":"低温 
24.0℃","sunset":"19:18","aqi":55.0,"fx":"东风","fl":"<3级","type":"阵雨",
"notice":"突如其来的雨,总叫让人措手不及"},{"date":"13日星期日",
"sunrise":"05:43","high":"高温 30.0℃","low":"低温 24.0℃",
"sunset":"19:17","aqi":60.0,"fx":"北风","fl":"<3级","type":"阵雨","notice":"突如其来的雨,总叫让人措手不及"},
{"date":"14日星期一","sunrise":"05:44","high":"高温 33.0℃",
"low":"低温 24.0℃","sunset":"19:16","aqi":62.0,"fx":"东北风","fl":"<3级",
"type":"晴","notice":"lovely sunshine,尽情享受阳光的温暖吧"},
{"date":"15日星期二","sunrise":"05:45","high":"高温 35.0℃",
"low":"低温 24.0℃","sunset":"19:14","aqi":65.0,"fx":"南风","fl":"<3级",
"type":"晴","notice":"天气干燥,请适当增加室内湿度"}]}}
  • 代码操作(二)- 爬取百度图片你的名字

先获取图片链接地址

# -*- coding:utf-8 -*-
import requests,re,urllib,urllib2


#定义引擎函数模块
def getImg(url):
    content = load_url(url)
    #数据获取完成,定义正则表达式,筛选数据
    reg = re.compile('"thumbURL":\s*"(.*?)"', re.S)
    data_list = data_filter(content,reg)
    #保存数据
    for index,data_url in enumerate(data_list):
        sava_data(data_url,"./images/" + str(index) + ".jpg")


#定义爬取数据的函数
def load_url(url):
    """
    作用:爬取指定url地址中的数据
    :param url: 指定的url地址
    :return: 所有爬取到的数据
    """
    print('开始爬取图片')
    my_headers = {
        'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',
    }
    response = requests.get(url,headers=my_headers,verify=False)
    response.encoding = 'UTF-8'
    content = response.text
    print('数据爬取完成')
    return content

#定义筛选数据的函数
def data_filter(data,reg):
    """
    作用:进行数据按照指定描述的正则筛选
    :param data: 所有数据
    :param reg: 正则表达式
    :return: 返回筛选到的数据列表
    """
    print('---开始筛选数据')
    data_list = reg.findall(data)
    print('筛选数据完成')
    return data_list


#定义保存数据的函数
def sava_data(url_content,file_name):
    """
    作用:保存数据
    :param url_content:需要保存的数据
    :param file_name: 文件名称
    :return:
    """
    print('开始保存数据')
    try:
        urllib.urlretrieve(url_content, file_name)

        #另外一种保存方式
        # my_headers = {
        #     'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',
        # }
        # print (url_content)
        # content = requests.get(url_content, headers=my_headers).content
        # with open(file_name, "wb") as f:
        #     f.write(content)
        print('图片下载成功')
    except Exception as result:
        print('图片下载失败'+str(result))
    print('数据保存完成')

#定义主程序入口
if __name__ == '__main__':
    #定义获取各种需要的参数数据
    url = 'https://image.baidu.com/search/acjson?tn=resultjson_com&ipn=rj&ct=201326592&is=&fp=result&queryWord=%E4%BD%A0%E7%9A%84%E5%90%8D%E5%AD%97&cl=2&lm=-1&ie=utf-8&oe=utf-8&adpicid=&st=-1&z=&ic=0&word=%E4%BD%A0%E7%9A%84%E5%90%8D%E5%AD%97&s=&se=&tab=&width=&height=&face=0&istype=2&qc=&nc=1&fr=&pn=30&rn=30&gsm=1e&1502454817018='
    #调用引擎对象,执行爬虫
    getImg(url)

结果:

你的名字

写到最后说些题外话,今天爬取一个网站的时候还发生了一件好玩的事,网站的反爬机制导致爬取的数据只是一个html网页,看到了页面上的话,我想起了一句话,对于爬虫工程师来说每天都是不停地和对方的反爬工程师斗智斗勇。
那么问题来了,到底什么是世界上最牛逼的语言

4.BeautifulSoup4

1.Beautiful Soup的简介

Beautiful Soup是python的一个库,最主要的功能是从网页抓取数据。官方解释如下:

  • Beautiful Soup提供一些简单的、python式的函数用来处理导航、搜索、修改分析树等功能。它是一个工具箱,通过解析文档为用户提供需要抓取的数据,因为简单,所以不需要多少代码就可以写出一个完整的应用程序。
  • Beautiful Soup自动将输入文档转换为Unicode编码,输出文档转换为utf-8编码。你不需要考虑编码方式,除非文档没有指定一个编码方式,这时,Beautiful Soup就不能自动识别编码方式了。然后,你仅仅需要说明一下原始编码方式就可以了。
  • Beautiful Soup已成为和lxml、html6lib一样出色的python解释器,为用户灵活地提供不同的解析策略或强劲的速度。

2. Beautiful Soup 安装

Beautiful Soup 3 目前已经停止开发,推荐在现在的项目中使用Beautiful Soup 4,不过它已经被移植到BS4了,也就是说导入时我们需要 import bs4 。所以这里我们用的版本是 Beautiful Soup 4.3.2 (简称BS4),另外据说 BS4 对 Python3 的支持不够好,不过我用的是 Python2.7.7,如果有小伙伴用的是 Python3 版本,可以考虑下载 BS3 版本。

  • 可以利用 pip 或者 easy_install 来安装,以下两种方法均可
easy_install beautifulsoup4
pip install beautifulsoup4
由于我的是python2和python3共存,所以安装时需加上python版本安装
  • 另一个可供选择的解析器是纯Python实现的 html5lib , html5lib的解析方式与浏览器相同,可以选择下列方法来安装html5lib:
easy_install html5lib
pip install html5lib

Beautiful Soup支持Python标准库中的HTML解析器,还支持一些第三方的解析器,如果我们不安装它,则 Python 会使用 Python默认的解析器,lxml 解析器更加强大,速度更快,推荐安装。

解析器 使用方法 优势 劣势
Python标准库 BeautifulSoup(markup, “html.parser”) Python的内置标准库,执行速度适中,文档容错能力强 Python 2.7.3 or 3.2.2)前的版本中文档容错能力差
lxml HTML 解析器 BeautifulSoup(markup, “lxml”) 速度快,文档容错能强 需要安装C语言库
lxml XML 解析器 BeautifulSoup(markup, [“lxml”,“xml”])BeautifulSoup(markup, “xml”) 速度快,唯一支持XML的解析器 需要安装C语言库
html5lib BeautifulSoup(markup, “html5lib”) 最好的容错性,以浏览器的方式解析文档,生成HTML5格式的文档 速度慢不依赖外部扩展

Beautiful Soup 4.4.0 文档官方文档

  • BeautifulSoup 主要用来遍历子节点及子节点的属性,通过点取属性的方式只能获得当前文档中的第一个 tag,例如,soup.li。如果想要得到所有的<li> 标签,或是通过名字得到比一个 tag 更多的内容的时候,就需要用到 find_all(),find_all() 方法搜索当前 tag 的所有 tag 子节点,并判断是否符合过滤器的条件find_all() 所接受的参数如下:
find_all( name , attrs , recursive , string , **kwargs )
  • find_all() 几乎是 Beautiful Soup中最常用的搜索方法,也可以使用其简写方法,以下代码等价:
soup.find_all("a")
soup("a")
  • 如果只想得到 tag 中包含的文本内容,那么可以只用 get_text() 方法,这个方法获取到 tag 中包含的所有文版内容包括子孙 tag 中的内容,并将结果作为 Unicode 字符串返回:
tag.p.a.get_text()

参考文档:Requests官方文档崔庆才老师的个人博客Beautiful Soup 4.4.0 文档 (中文文档)

如果你觉得我的文章还可以,可以关注我的微信公众号:Python攻城狮

可扫描二维码,添加关注

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

推荐阅读更多精彩内容