爬虫 >>> Requests 模块

爬虫的定义

  向网站发起请求,获取资源后分析并提取 有用数据 (我们的爬虫程序只提取网页代码中对我们有用的数据)

爬虫的基本流程

                   

1、发起请求
    使用 http 库向目标站点发起请求,即发送一个 Request
    Request 包含:请求头、请求体等
    注:selenium 也是经常用到的模块,可以解析 html 页面

2、获取响应内容
    如果服务器能正常响应,则会得到一个 Response
    Response 包含:html,json,图片,视频等

3、解析内容
    解析 html 数据:正则表达式,第三方解析库如 Beautifulsoup,pyquery 等
    解析 json 数据:json 模块
    解析二进制数据:以 b 模式写入文件

4、保存数据
    保存在数据库: MySQL, mongoDB, redis等
    或者文件中

 官网链接: https://2.python-requests.org//en/master/

 下载安装:pip3 install requests


 

Requests

  1:发送 GET 和 POST 请求(常用) 

      使用 request 发送 get 和 post 请求,访问的数据都会拼接成这种形式:k1=xxx&k2=yyy&k3=zzz

      post 请求的参数存放在请求体中(可以用浏览器查看,存放于 form-data 中)

      get 请求的参数直接 放在 url 后面,可以直接查看到

   2:请求头

      User-Agent:绝大多数网站需要设置 User-Agent;缺失的话可能会将你当作非法用户

      在请求头内将自己伪装成浏览器,否则百度不会正常返回页面内容

      Cookies:用来保存登录信息

      Referer:访问当前链接的上一层链接(校验作用),用来判断请求来源

      Host: 不一定需要

import requests

response = requests.get/post(url, headers, cookie, params/data, stream)  
'''
url:要访问的路径
headers:请求时候的请求头,一般要携带 User-Agent, Referer 等参数
cookie:每次请求的 token 值,本质是包含在 headers 内,但为了方便,可以单独书写
params:get 请求所携带的参数,会自动往要访问的 url 后面进行拼接(会暴露在浏览器的地址栏中)
data:post 请求所携带的参数
stream:访问视频资源较大时,设置参数为 True,可以一点一点获取资源,默认为 False
...
'''

补充:

 HTTP 默认的请求方式是 get 请求:
   get 请求没有请求体,数据必须在1k之内,并且数据会暴露在浏览器的地址栏中
   requests.post() 用法与 requests.get() 一致 

   GET 和 POST 的区别:

    

requests.post(url='xxxxxxxx', data={'xxx':'yyy'}) 
# 没有指定请求头, 默认的请求头:application/x-www-form-urlencoed

# 如果我们自定义请求头是 application/json,并且用 data 传值, 则服务端取不到值
requests.post(url='xxx', data={'pwd':1}, headers={'content-type':'application/json'})
requests.post(url='xxx',json={'pwd':1})  # 默认的请求头:application/json

   响应相关

response.encoding =  # 默认编码为ISO-8859-1(如若请求来的内容有中文,需要指定一下编码格式)
response.status_code  # 相应状态码
response.text  # 响应的内容文本
response.content  # 响应的二进制数据:如图片,视频等资源
response.headers  # 响应头信息
response.cookies  # 相应回来的cookies
response.cookies.get_dict()  # 获取cookies信息,值为字典形式
response.cookies.items()
response.url  # 响应回来的url
response.history  # 发送响应到接收响应经过的所有url
response.close()  # 关闭响应
...

  响应状态

    200:成功

    301:跳转

    404:资源不存在

    403:权限不足

    502:服务器错误

   解析 json

import requests
import json

response=requests.get('http://httpbin.org/get')
res1 = json.loads(response.text)  # 太麻烦
res2 = response.json()  # 直接获取 json 数据
print(res1 == res2)  # 结果为 True

   例子:爬取校花网视频

import requests
import re
import time
import hashlib


def get_page(url):
    print('GET %s' % url)
    try:
        response = requests.get(url)
        if response.status_code == 200:
            return response.content
    except Exception:
        pass

def parse_index(res):
    obj = re.compile('class="items.*?<a href="(.*?)"', re.S)
    detail_urls = obj.findall(res.decode('gbk'))
    for detail_url in detail_urls:
        if not detail_url.startswith('http'):
            detail_url = 'http://www.xiaohuar.com' + detail_url
        yield detail_url

def parse_detail(res):
    obj = re.compile('id="media".*?src="(.*?)"', re.S)
    res = obj.findall(res.decode('gbk'))
    if len(res) > 0:
        movie_url = res[0]
        return movie_url

def save(movie_url):
    response = requests.get(movie_url, stream=False)
    if response.status_code == 200:
        m = hashlib.md5()
        m.update(('%s%s.mp4' % (movie_url, time.time())).encode('utf-8'))
        filename = m.hexdigest()
        with open(r'./movies/%s.mp4' % filename, 'wb') as f:
            f.write(response.content)
            f.flush()

def main():
    index_url = 'http://www.xiaohuar.com/list-3-{0}.html'
    for i in range(5):
        print('*' * 50, i)
        # 爬取主页面
        index_page = get_page(index_url.format(i, ))
        # 解析主页面,拿到视频所在的地址列表
        detail_urls = parse_index(index_page)
        # 循环爬取视频页
        for detail_url in detail_urls:
            # 爬取视频页
            detail_page = get_page(detail_url)
            # 拿到视频的url
            movie_url = parse_detail(detail_page)
            if movie_url:
                # 保存视频
                save(movie_url)

if __name__ == '__main__':
    main()

    
# 并发爬取
from concurrent.futures import ThreadPoolExecutor
import queue
import requests
import re
import time
import hashlib
from threading import current_thread


p = ThreadPoolExecutor(50)
def get_page(url):
    print('%s GET %s' % (current_thread().getName(), url))
    try:
        response = requests.get(url)
        if response.status_code == 200:
            return response.content
    except Exception as e:
        print(e)

def parse_index(res):
    print('%s parse index ' % current_thread().getName())
    res = res.result()
    obj = re.compile('class="items.*?<a href="(.*?)"', re.S)
    detail_urls = obj.findall(res.decode('gbk'))
    for detail_url in detail_urls:
        if not detail_url.startswith('http'):
            detail_url = 'http://www.xiaohuar.com' + detail_url
        p.submit(get_page, detail_url).add_done_callback(parse_detail)

def parse_detail(res):
    print('%s parse detail ' % current_thread().getName())
    res = res.result()
    obj = re.compile('id="media".*?src="(.*?)"', re.S)
    res = obj.findall(res.decode('gbk'))
    if len(res) > 0:
        movie_url = res[0]
        print('MOVIE_URL: ', movie_url)
        with open('db.txt', 'a') as f:
            f.write('%s
' % movie_url)
        # save(movie_url)
        p.submit(save, movie_url)
        print('%s下载任务已经提交' % movie_url)

def save(movie_url):
    print('%s SAVE: %s' % (current_thread().getName(), movie_url))
    try:
        response = requests.get(movie_url, stream=False)
        if response.status_code == 200:
            m = hashlib.md5()
            m.update(('%s%s.mp4' % (movie_url, time.time())).encode('utf-8'))
            filename = m.hexdigest()
            with open(r'./movies/%s.mp4' % filename, 'wb') as f:
                f.write(response.content)
                f.flush()
    except Exception as e:
        print(e)

def main():
    index_url = 'http://www.xiaohuar.com/list-3-{0}.html'
    for i in range(5):
        p.submit(get_page, index_url.format(i, )).add_done_callback(parse_index)

if __name__ == '__main__':
    main()

   高级用法

    SSL Cert Verification(验证证书)

'''
很多网站都是 https,但是不用证书也可以访问,大多数情况都是可以携带也可以不携带证书
知乎百度等都是可带可不带
有硬性要求的,则必须带,比如对于定向的用户,拿到证书后才有权限访问某个特定网站
'''

# 如果是 ssl 请求,首先检验证书是否合法;不合法则报错,程序终止
import requests
response = requests.get('https://www.12306.cn')  


# 改进1:
import requests
response = requests.get('https://www.12306.cn', verify=False)
print(response.status_code)  # 不验证证书,报警告,返回200


# 改进2:
import requests
from requests.packages import urllib3
urllib3.disable_warnings()  # 关闭警告
respone=requests.get('https://www.12306.cn',verify=False)
print(respone.status_code)  # 200


# 改进3:加上证书
import requests
respone=requests.get('https://www.12306.cn', cert=('/path/server.crt', '/path/key'))
print(respone.status_code)

   使用代理

     官网链接: http://docs.python-requests.org/en/master/user/advanced/#proxies

# 代理设置:先发送请求给代理,然后由代理帮忙发送(封ip是常见的事情)
import requests
proxies={'http':'http://egon:123@localhost:9743', # 随机从代理池取出一个IP发送请求
         # 带用户名密码的代理,@符号前是用户名与密码
        'http':'http://localhost:9743',
        'https':'https://localhost:9743'}

respone=requests.get('https://www.12306.cn', proxies=proxies)
print(respone.status_code)


# 支持socks代理,安装:pip install requests[socks]
import requests
proxies = {'http': 'socks5://user:pass@host:port',
           'https': 'socks5://user:pass@host:port'}

respone=requests.get('https://www.12306.cn', proxies=proxies)
print(respone.status_code)

   超时设置

# 两种超时:float or tuple
# timeout=0.1 代表接收数据的超时时间
# timeout=(0.1, 0.2)   0.1代表链接超时  0.2代表接收数据的超时时间

import requests
respone=requests.get('https://www.baidu.com', timeout=0.0001)

  异常处理 

import requests
from requests.exceptions import * # 可以查看requests.exceptions 获取异常类型

try:
    r = requests.get('http://www.baidu.com', timeout=0.00001)
except ReadTimeout:
    print('===:')
except ConnectionError:  # 网络不通
     print('-----')
except Timeout:
     print('aaaaa')
except RequestException:
    print('Error')

  上传文件

import requests

files = {'file': open('a.jpg', 'rb')}
response = requests.post('http://httpbiin.org/post', files=files)
print(respone.status_code)
原文地址:https://www.cnblogs.com/pupy/p/11989587.html