爬虫D4 requests库的使用


I. 简介

Requests是有史以来下载次数最多的Python软件包之一,每天下载量超过400,000次。

Requests的官方文档同样也非常的完善详尽,而且少见的有中文官方文档:http://cn.python-requests.org/zh_CN/latest/。

英文文档:http://docs.python-requests.org/en/master/api/

Requests的作者是Kenneth Reitz

使用requests库需要事先安装:
pip install requests

II. 发起请求

i. 请求方法

Requests的请求不再像urllib一样需要去构造各种Request、opener和handler,使用Requests构造的方法,并在其中传入需要的参数即可。

每一个请求方法都有一个对应的API,比如GET请求就可以使用get()方法。

1. get请求
import requests:
resp = requests.get('https://www.baidu.com')

当需要向后缀的内容(比如通过百度搜python)发起请求时应该怎么写呢?

step 1 先将对应的url复制,找到关键字段s?wd=python

step 2 创建形参params,再传入requests.get调用

params = {'wd': 'python'}
requests.get('https://www.baidu.com/s?', params=params)

这样就能访问到相关的内容了。

但是有些网站会出现403禁止访问的问题,需要访问权限。

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

此时返回的内容是:
<Response [403]>

这种情况就需要加headers了。headeers要填入的内容可以通过浏览器登录httpbin.org/get来获取:
header_fill_in

headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36"
}

r = requests.get('https://www.jianshu.com/', headers=headers)
print(r)

此时运行结果是:<Response [200]>

返回文本:

print(r.text)

返回二进制:

print(r.content)

当出现乱码时,可以加一段r.encoding = 'utf8'

查看网页代码时可以输入:

print(r.encoding)

Back

2. post方法:

post的常规格式如下:

resp = requests.post(url='', data=)

这里以http://httpbin.org/post为例。把该url输入浏览器,点击回车,页面会显示"The method is not allowed for the requested URL."。为什么会出现这种问题?因为以上操作实际上都是get请求,而不是post。所以如果要打印出来相应的内容,只能通过print(r.text)

import requests
...
data = {'username':'Spiderman'}

r = requests.post(url='http://httpbin.org/post',data=data)
print(r.text)

打印结果是:

{
  "args": {}, 
  "data": "", 
  "files": {}, 
  "form": {
    "username": "Spiderman"
  }, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Content-Length": "13", 
    "Content-Type": "application/x-www-form-urlencoded", 
    "Host": "httpbin.org", 
    "User-Agent": "python-requests/2.21.0", 
    "X-Amzn-Trace-Id": "Root=1-5e78263f-703d0f1c7152ac7c5f4bd4f0"
  }, 
  "json": null, 
  "origin": "183.6.116.33", 
  "url": "http://httpbin.org/post"
}

输出结果可以看出,其数据类型是字符串。可以通过print(type(r.text))查询出来。同时这个数据也是一个json方法获取的数据。所以也可以通过print(r.json)来打印,但是显示结果是字典。此时再用````print(type(r.json))```查询出来显示是dict类型。

注意:python里并没有json类型的数据,json主要用于前台。而json方法是可以被python调用的。

Back

3. 自定义Cookies
url = 'http://www.baidu.com/cookies'
cookies = {'cookie': 'BIDUPSID=23DB26043207FD9E258CF9AD2EF83046; PSTM=1550374946; BDUSS=DU2M1VyMkl5bjNXenNpbWRVUXFsRjU5ajlGb1ZNQkRPYmhmQkcwTDRoVURGY2xjQVFBQUFBJCQAAAAAAAAAAAEAAABSbbJRwK23qMKzAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOIoVwDiKFcc; MCITY=-%3A; BD_UPN=12314753; BAIDUID=1DEAB40A7C0DCDBBDA02B82DF1B5B65A:FG=1; BDORZ=B490B5EBF6F3CD402E515D22BCDA1598; shifen[89083533270_8408]=1584956655; BCLID=9015116421683587576; BDSFRCVID=SwFOJeC62AYd6Z5uf0YZUGQBJmJnmZcTH6aIcw4ZvwhJyapsWLBJEG0Pjf8g0KubtwsaogKK3gOTH4DF_2uxOjjg8UtVJeC6EG0Ptf8g0M5; H_BDCLCKID_SF=tJ48_CLMfC_3fP36q4rMM-LthfLX5-RLfa7fKq7F5l8-hljXb5jAjJLEDbrzLxbI0eQLaJrEyJOxOKQpytbbjhIB5U7Zh4oj-avr5qcN3KJmjRL9bT3v5tD35pbK2-biWb7M2MbdJpbP_IoG2Mn8M4bb3qOpBtQmJeTxoUJ25DnJhhCGe4bK-TrLjaLJtU5; __guid=136081015.4240479547906954000.1584956654804.911; BD_HOME=1; monitor_count=2; BDRCVFR[feWj1Vr5u3D]=I67x6TjHwwYf0; delPer=0; BD_CK_SAM=1; PSINO=7; H_PS_PSSID=30968_1447_31124_21116_30903_30824_31086_26350; H_PS_645EC=0c33BVWenHG4utxsBfFYWJ5l69VWujaNT2gZsRI9%2F56QHP3SWoM4s5QMNmUBACagqrgf; BDSVRTM=119;'}

resp = requests.get(url, cookies=cookies)
print(resp.text)
  • 如何获取网页中的cookie信息?见下图:
    howtogetcookie

Back

4. 设置代理

在需要使用代理时,构造代理字典,传递给proxies参数

import requests

proxies = {
    'http':'http://10.10.1.10:3128',
    'https':'https//10.10.1.10:1080',
}
requests.get('http://httpbin.org', proxies=proxies)
5. 重定向

在网络请求中,我们常常会遇到状态码是3开头的重定向问题,在Requests中是默认开启允许重定向的,即遇到重定向时,会自动继续访问。

import requests

resp = request.get('http://github.com', allow_redirects=False)
print(resp.status_code)

Back

III. 百度贴吧案例

创建session对象

session = requests.Session()

session.get()

session.post()

session调用和request调用的区别:request调用无法自动保存cookie数据;而session调用可以自动保存cookie数据,只有在关闭了session对话的情况下,cookie数据才会过期。

百度贴吧数据获取。这里以华为吧为例:

最初始的动作是导入requests库import requests。因为还会用到正则,故还需导入re库,写法同前。

step 1 进入目标贴吧,点翻页到下一页。点击浏览器地址栏,复制url,粘贴到记事本备用。格式是https://tieba.baidu.com/f?kw=%E5%8D%8E%E4%B8%BA&ie=utf-8&pn=50。可以把涉及编码、格式等内容的字符串去掉,也就是保留f?kw=%E5%8D%8E%E4%B8%BA&pn=50的部分(pn=50表示第一页前50条)

resp = requests.get('https://tieba.baidu.com/f?kw=%E5%8D%8E%E4%B8%BA&pn=50').text

step 2 匹配出每一个帖子的url:随机找一篇文章新标签页打开。点击浏览器地址栏,会发现url地址是https://tieba.baidu.com/p/6568xxxxxx的格式,复制这段数字。点击鼠标右键选“检查”,填入该数字查找标题后搜索,找到的对应的标题代码<a rel="noreferrer" href="/p/6568xxxxxx"。复制该段代码粘贴至记事本备用。
search_title

通过findall方法匹配帖子标题,将<a rel="noreferrer" href="/p/6568xxxxxx"填入r''中,并改写/p/后面的内容(因为是全数字,故使用d+进行匹配)

article_urls = re.findall(r'<a rel="noreferrer" href="(/p/\d+)"', resp, re.S)

step 3 url拼接

article_urls = ['https://tieba.baidu.com/'+url for url in article_urls]

step 4 对帖子的详情页面发起请求

article_resp = requests.get(article_urls[0]).text

step 5 解析想要的具体数据

我们尝试从title, author和create_time三个要素进行解析:

a. 先解析title:点开一篇贴吧文章,复制标题,点击右键查看源代码,然后ctrl+f,弹出搜索框,此时可以查找想要找的<title>标签,复制标题<title>华为P40系列三个版本后置摄像头参数被曝光!_华为吧_百度贴吧备用。

在pycharm里输入以下代码:

title = re.findall(r'<title>(.*?)_华为吧_百度贴吧, article_resp, re.S')[0]  
print(title) # print用于检查代码是否能有效工作,检查ok后可删除

注意_华为吧_百度贴吧这部分字段要保留,仅标题用(.*?)代替,否则打印时无任何内容显示

b. 解析author:复制一楼的作者名,在源代码页搜索框中输入作者名查找。此时出来的页面如下:
identify_username_step1

但这个网名并非我们要复制的字段,那应该怎么找呢?此时往前看会发现FF9Ps52xe1这个字段,而在原帖检查页面上页看到了user_name后面对应的也是这个字符串。
identify_user_name_step2.jpg)

把这个字段复制后再在源代码页面搜索,此时找到了author:“FF9Ps52xe1”,这段才是我们要复制的内容。
identify_user_name_step3.jpg

那么author部分的代码则按如下写法:

author = re.findall(r'author:"(.*?)"', article_resp, re.S)[0]

注意: author:"(.*?)"的双引号后面可以再跟一个英文逗号,也可以不加。

c. 解析创建事件create_time:复制帖子1楼的时间,在源代码页搜索框中粘贴搜搜。找到&quot;2020-03-22 10:20&quot的字段复制,然后粘贴等到以下代码的r''中,再将具体的时间替换为(\d+-\d+-\d+ \d+:\d+)
create_time = re.findall(r'&quot;(\d+-\d+-\d+ \d+:\d+)', article_resp, re.S)[0]

接下来就可以打印想要的信息了:

print(title, ': ', author)
print('发布时间:{}'.format(create_time))

print_title_author_createtime

而要匹配出多篇文章,则需要用到遍历,完整的代码如下:

import re
import requests

resp = requests.get('https://tieba.baidu.com/f?kw=%E5%8D%8E%E4%B8%BA&pn=50').text

# 匹配出每一个帖子的url
article_urls = re.findall(r'<a rel="noreferrer" href="(/p/\d+)"', resp, re.S)  # 单引号里的a rel的内容是通过浏览器页面解析查找出来的

# url拼接
article_urls = ['https://tieba.baidu.com/'+url for url in article_urls]

for url in article_urls:
    # 对帖子的详情页面发起请求
    article_resp = requests.get(url).text
    # 解析出想要的具体数据
    title = re.findall(r'<title>(.*?)_华为吧_百度贴吧', article_resp, re.S)[0]
    
    author = re.findall(r'author:"(.*?)"', article_resp, re.S)[0]

    create_time = re.findall(r'&quot;(\d{4}-\d+-\d+ \d+:\d+)&quot', article_resp, re.S)[0]

    print(title, ':', author)
    print('发布时间:{}'.format(create_time))

Back

如果我们想让这个爬虫程序也能用于爬取其他贴吧的内容,并且能够跳转到想要的页码去,应该怎么改程序呢?

"""
1. kw=和pn=要改写成变量,并加入.format(key, pn*50)使字段可以进行填充。
2. pn*50表示每页50条标题,翻页就是*50。
"""
key = input('请输入要获取的贴吧: ')

page = input('请输入要获取的页数: ')

for pn in range(int(page)):  #此处页码需要转换为整数int,否则会报错
    resp = requests.get('https://tieba.baidu.com/f?kw={}&pn={}'.format(key, pn*50)).text   
    

    # 匹配出每一个帖子的url
    article_urls = re.findall(r'<a rel="noreferrer" href="(/p/\d+)"', resp, re.S)  # 单引号里的a rel的内容是通过浏览器页面解析查找出来的

    # url拼接
    article_urls = ['https://tieba.baidu.com'+url for url in article_urls]

    for url in article_urls:  # 遍历url
        # 对帖子的详情页面发起请求
        article_resp = requests.get(url).text  # url作为变量传入get请求中
        # 解析出想要的具体数据
        title = re.findall(r"'threadTitle': '(.*?)'", article_resp, re.S)[0]
        """
        因为之前<title>标签匹配不到标题,这次在源代码中找到新的标题匹配标识,改用threadTitle
        """

        try:
            author = re.findall(r'author:"(.*?)"', article_resp, re.S)[0]
        except IndexError as e:
            print('数据匹配异常')
            continue

        create_time = re.findall(r'quot;(\d{4}-\d+-\d+ \d{2}:\d{2})&quot', article_resp, re.S)
        if not create_time: # 如果以上规则不适用
            print('第二次匹配时间')
            create_time = re.findall(r'<span class="tail-info">(\d{4}-\d+-\d+ \d+:\d+)</span', article_resp, re.S)  # 另一种匹配规则,因为不同吧的时间标签不一样

        print(title, ':', author)
        print('发布时间:{}'.format(create_time[0]))  # 把索引加到这里以形参形式传入,是为了避免前面出错时不再往下运行

tieba_articles_run

Back

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值