pythonrequest得替代_Python爬虫的requests模块你真的学会了吗?来看看这些高级用法!...

本文介绍了Python requests模块的高级用法,包括文件上传、Cookies操作、会话维持以及SSL证书验证、代理设置、超时控制和身份认证。重点讲述了使用Session对象保持会话状态,避免多次设置Cookies的繁琐,以及如何处理SSL证书验证错误。同时,还提及了PreparedRequest,允许将请求作为独立的数据结构进行操作,方便队列调度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 文件上传

我们知道requests可以模拟提交一些数据。假如有的网站需要上传文件,我们也可以用它来实现,这非常简单,示例如下:

import requests

files = {'file': open('favicon.ico', 'rb')}

r = requests.post("http://httpbin.org/post", files=files)

print(r.text)

在前一节中我们保存了一个文件favicon.ico,这次用它来模拟文件上传的过程。需要注意的是,favicon.ico需要和当前脚本在同一目录下。如果有其他文件,当然也可以使用其他文件来上传,更改下代码即可。

运行结果如下:

{

"args": {},

"data": "",

"files": {

"file": "data:application/octet-stream;base64,AAAAAA...="

},

"form": {},

"headers": {

"Accept": "*/*",

"Accept-Encoding": "gzip, deflate",

"Content-Length": "6665",

"Content-Type": "multipart/form-data; boundary=809f80b1a2974132b133ade1a8e8e058",

"Host": "httpbin.org",

"User-Agent": "python-requests/2.10.0"

},

"json": null,

"origin": "60.207.237.16",

"url": "http://httpbin.org/post"

}

以上省略部分内容,这个网站会返回响应,里面包含files这个字段,而form字段是空的,这证明文件上传部分会单独有一个files字段来标识。

2. Cookies

前面我们使用urllib处理过Cookies,写法比较复杂,而有了requests,获取和设置Cookies只需一步即可完成。

我们先用一个实例看一下获取Cookies的过程:

import requests

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

print(r.cookies)

for key, value in r.cookies.items():

print(key + '=' + value)

运行结果如下:

, ]>

BDORZ=27315

__bsi=13533594356813414194_00_14_N_N_2_0303_C02F_N_N_N_0

这里我们首先调用cookies属性即可成功得到Cookies,可以发现它是RequestCookieJar类型。然后用items()方法将其转化为元组组成的列表,遍历输出每一个Cookie的名称和值,实现Cookie的遍历解析。

当然,我们也可以直接用Cookie来维持登录状态,下面以知乎为例来说明。首先登录知乎,将Headers中的Cookie内容复制下来,如图3-6所示。

图3-6 Cookie

这里可以替换成你自己的Cookie,将其设置到Headers里面,然后发送请求,示例如下:

import requests

headers = {

'Cookie': 'q_c1=31653b264a074fc9a57816d1ea93ed8b|1474273938000|1474273938000; d_c0="AGDAs254kAqPTr6NW1U3XTLFzKhMPQ6H_nc=|1474273938"; __utmv=51854390.100-1|2=registration_date=20130902=1^3=entry_date=20130902=1;a_t="2.0AACAfbwdAAAXAAAAso0QWAAAgH28HQAAAGDAs254kAoXAAAAYQJVTQ4FCVgA360us8BAklzLYNEHUd6kmHtRQX5a6hiZxKCynnycerLQ3gIkoJLOCQ==";z_c0=Mi4wQUFDQWZid2RBQUFBWU1DemJuaVFDaGNBQUFCaEFsVk5EZ1VKV0FEZnJTNnp3RUNTWE10ZzBRZFIzcVNZZTFGQmZn|1474887858|64b4d4234a21de774c42c837fe0b672fdb5763b0',

'Host': 'www.zhihu.com',

'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.116 Safari/537.36',

}

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

print(r.text)

我们发现,结果中包含了登录后的结果,如图3-7所示,这证明登录成功。

图3-7 运行结果

当然,你也可以通过cookies参数来设置,不过这样就需要构造RequestsCookieJar对象,而且需要分割一下cookies。这相对繁琐,不过效果是相同的,示例如下:

import requests

cookies = 'q_c1=31653b264a074fc9a57816d1ea93ed8b|1474273938000|1474273938000; d_c0="AGDAs254kAqPTr6NW1U3XTLFzKhMPQ6H_nc=|1474273938"; __utmv=51854390.100-1|2=registration_date=20130902=1^3=entry_date=20130902=1;a_t="2.0AACAfbwdAAAXAAAAso0QWAAAgH28HQAAAGDAs254kAoXAAAAYQJVTQ4FCVgA360us8BAklzLYNEHUd6kmHtRQX5a6hiZxKCynnycerLQ3gIkoJLOCQ==";z_c0=Mi4wQUFDQWZid2RBQUFBWU1DemJuaVFDaGNBQUFCaEFsVk5EZ1VKV0FEZnJTNnp3RUNTWE10ZzBRZFIzcVNZZTFGQmZn|1474887858|64b4d4234a21de774c42c837fe0b672fdb5763b0'

jar = requests.cookies.RequestsCookieJar()

headers = {

'Host': 'www.zhihu.com',

'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.116 Safari/537.36'

}

for cookie in cookies.split(';'):

key, value = cookie.split('=', 1)

jar.set(key, value)

r = requests.get("http://www.zhihu.com", cookies=jar, headers=headers)

print(r.text)

这里我们首先新建了一个RequestCookieJar对象,然后将复制下来的cookies利用split()方法分割,接着利用set()方法设置好每个Cookie的key和value,然后通过调用requests的get()方法并传递给cookies参数即可。当然,由于知乎本身的限制,headers参数也不能少,只不过不需要在原来的headers参数里面设置cookie字段了。

测试后,发现同样可以正常登录知乎。

3. 会话维持

在requests中,如果直接利用get()或post()等方法的确可以做到模拟网页的请求,但是这实际上是相当于不同的会话,也就是说相当于你用了两个浏览器打开了不同的页面。

设想这样一个场景,第一个请求利用post()方法登录了某个网站,第二次想获取成功登录后的自己的个人信息,你又用了一次get()方法去请求个人信息页面。实际上,这相当于打开了两个浏览器,是两个完全不相关的会话,能成功获取个人信息吗?那当然不能。

有小伙伴可能说了,我在两次请求时设置一样的cookies不就行了?可以,但这样做起来显得很烦琐,我们有更简单的解决方法。

其实解决这个问题的主要方法就是维持同一个会话,也就是相当于打开一个新的浏览器选项卡而不是新开一个浏览器。但是我又不想每次设置cookies,那该怎么办呢?这时候就有了新的利器——Session对象。

利用它,我们可以方便地维护一个会话,而且不用担心cookies的问题,它会帮我们自动处理好。示例如下:

import requests

requests.get('http://httpbin.org/cookies/set/number/123456789')

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

print(r.text)

这里我们请求了一个测试网址http://httpbin.org/cookies/set/number/123456789。请求这个网址时,可以设置一个cookie,名称叫作number,内容是123456789,随后又请求了http://httpbin.org/cookies,此网址可以获取当前的Cookies。

这样能成功获取到设置的Cookies吗?试试看。

运行结果如下:

{

"cookies": {}

}

这并不行。我们再用Session试试看:

import requests

s = requests.Session()

s.get('http://httpbin.org/cookies/set/number/123456789')

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

print(r.text)

再看下运行结果:

{

"cookies": {

"number": "123456789"

}

}

成功获取!这下能体会到同一个会话和不同会话的区别了吧!

所以,利用Session,可以做到模拟同一个会话而不用担心Cookies的问题。它通常用于模拟登录成功之后再进行下一步的操作。

Session在平常用得非常广泛,可以用于模拟在一个浏览器中打开同一站点的不同页面,后面会有专门的章节来讲解这部分内容。

4. SSL证书验证

此外,requests还提供了证书验证的功能。当发送HTTP请求的时候,它会检查SSL证书,我们可以使用verify参数控制是否检查此证书。其实如果不加verify参数的话,默认是True,会自动验证。

前面我们提到过,12306的证书没有被官方CA机构信任,会出现证书验证错误的结果。我们现在访问它,都可以看到一个证书问题的页面,如图3-8所示。

图3-8 错误页面

现在我们用requests来测试一下:

import requests

response = requests.get('https://www.12306.cn')

print(response.status_code)

运行结果如下:

requests.exceptions.SSLError: ("bad handshake: Error([('SSL routines', 'tls_process_server_certificate', 'certificate verify failed')],)",)

这里提示一个错误SSLError,表示证书验证错误。所以,如果请求一个HTTPS站点,但是证书验证错误的页面时,就会报这样的错误,那么如何避免这个错误呢?很简单,把verify参数设置为False即可。相关代码如下:

import requests

response = requests.get('https://www.12306.cn', verify=False)

print(response.status_code)

这样就会打印出请求成功的状态码:

/usr/local/lib/python3.6/site-packages/urllib3/connectionpool.py:852: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings

InsecureRequestWarning)

200

不过我们发现报了一个警告,它建议我们给它指定证书。我们可以通过设置忽略警告的方式来屏蔽这个警告:

import requests

from requests.packages import urllib3

urllib3.disable_warnings()

response = requests.get('https://www.12306.cn', verify=False)

print(response.status_code)

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

import logging

import requests

logging.captureWarnings(True)

response = requests.get('https://www.12306.cn', verify=False)

print(response.status_code)

当然,我们也可以指定一个本地证书用作客户端证书,这可以是单个文件(包含密钥和证书)或一个包含两个文件路径的元组:

import requests

response = requests.get('https://www.12306.cn', cert=('/path/server.crt', '/path/key'))

print(response.status_code)

当然,上面的代码是演示实例,我们需要有crt和key文件,并且指定它们的路径。注意,本地私有证书的key必须是解密状态,加密状态的key是不支持的。

5. 代理设置

对于某些网站,在测试的时候请求几次,能正常获取内容。但是一旦开始大规模爬取,对于大规模且频繁的请求,网站可能会弹出验证码,或者跳转到登录认证页面,更甚者可能会直接封禁客户端的IP,导致一定时间段内无法访问。

那么,为了防止这种情况发生,我们需要设置代理来解决这个问题,这就需要用到proxies参数。可以用这样的方式设置:

import requests

proxies = {

"http": "http://10.10.1.10:3128",

"https": "http://10.10.1.10:1080",

}

requests.get("https://www.taobao.com", proxies=proxies)

当然,直接运行这个实例可能不行,因为这个代理可能是无效的,请换成自己的有效代理试验一下。

若代理需要使用HTTP Basic Auth,可以使用类似http://user:password@host:port这样的语法来设置代理,示例如下:

import requests

proxies = {

"http": "http://user:password@10.10.1.10:3128/",

}

requests.get("https://www.taobao.com", proxies=proxies)

除了基本的HTTP代理外,requests还支持SOCKS协议的代理。

首先,需要安装socks这个库:

pip3 install 'requests[socks]'

然后就可以使用SOCKS协议代理了,示例如下:

import requests

proxies = {

'http': 'socks5://user:password@host:port',

'https': 'socks5://user:password@host:port'

}

requests.get("https://www.taobao.com", proxies=proxies)

6. 超时设置

在本机网络状况不好或者服务器网络响应太慢甚至无响应时,我们可能会等待特别久的时间才可能收到响应,甚至到最后收不到响应而报错。为了防止服务器不能及时响应,应该设置一个超时时间,即超过了这个时间还没有得到响应,那就报错。这需要用到timeout参数。这个时间的计算是发出请求到服务器返回响应的时间。示例如下:

import requests

r = requests.get("https://www.taobao.com", timeout = 1)

print(r.status_code)

通过这样的方式,我们可以将超时时间设置为1秒,如果1秒内没有响应,那就抛出异常。

实际上,请求分为两个阶段,即连接(connect)和读取(read)。

上面设置的timeout将用作连接和读取这二者的timeout总和。

如果要分别指定,就可以传入一个元组:

r = requests.get('https://www.taobao.com', timeout=(5,11, 30))

如果想永久等待,可以直接将timeout设置为None,或者不设置直接留空,因为默认是None。这样的话,如果服务器还在运行,但是响应特别慢,那就慢慢等吧,它永远不会返回超时错误的。其用法如下:

r = requests.get('https://www.taobao.com', timeout=None)

或直接不加参数:

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

7. 身份认证

在访问网站时,我们可能会遇到这样的认证页面,如图3-9所示。

图3-9 认证页面

此时可以使用requests自带的身份认证功能,示例如下:

import requests

from requests.auth import HTTPBasicAuth

r = requests.get('http://localhost:5000', auth=HTTPBasicAuth('username', 'password'))

print(r.status_code)

如果用户名和密码正确的话,请求时就会自动认证成功,会返回200状态码,如果认证失败,则返回401状态码。

当然,如果参数都传一个HTTPBasicAuth类,就显得有点烦琐了,所以requests提供了一个更简单的写法,可以直接传一个元组,它会默认使用HTTPBasicAuth这个类来认证。

所以上面的代码可以直接简写如下:

import requests

r = requests.get('http://localhost:5000', auth=('username', 'password'))

print(r.status_code)

此外,requests还提供了其他认证方式,如OAuth认证,不过此时需要安装oauth包,安装命令如下:

pip3 install requests_oauthlib

使用OAuth1认证的方法如下:

import requests

from requests_oauthlib import OAuth1

url = 'https://api.twitter.com/1.1/account/verify_credentials.json'

auth = OAuth1('YOUR_APP_KEY', 'YOUR_APP_SECRET',

'USER_OAUTH_TOKEN', 'USER_OAUTH_TOKEN_SECRET')

requests.get(url, auth=auth)

8. Prepared Request

前面介绍urllib时,我们可以将请求表示为数据结构,其中各个参数都可以通过一个Request对象来表示。这在requests里同样可以做到,这个数据结构就叫Prepared Request。我们用实例看一下:

from requests import Request, Session

url = 'http://httpbin.org/post'

data = {

'name': 'germey'

}

headers = {

'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.116 Safari/537.36'

}

s = Session()

req = Request('POST', url, data=data, headers=headers)

prepped = s.prepare_request(req)

r = s.send(prepped)

print(r.text)

这里我们引入了Request,然后用url、data和headers参数构造了一个Request对象,这时需要再调用Session的prepare_request()方法将其转换为一个Prepared Request对象,然后调用send()方法发送即可,运行结果如下:

{

"args": {},

"data": "",

"files": {},

"form": {

"name": "germey"

},

"headers": {

"Accept": "*/*",

"Accept-Encoding": "gzip, deflate",

"Connection": "close",

"Content-Length": "11",

"Content-Type": "application/x-www-form-urlencoded",

"Host": "httpbin.org",

"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.116 Safari/537.36"

},

"json": null,

"origin": "182.32.203.166",

"url": "http://httpbin.org/post"

}

可以看到,我们达到了同样的POST请求效果。

有了Request这个对象,就可以将请求当作独立的对象来看待,这样在进行队列调度时会非常方便。后面我们会用它来构造一个Request队列。

本节讲解了requests的一些高级用法,这些用法在后面实战部分会经常用到,需要熟练掌握。更多的用法可以参考requests的官方文档:http://docs.python-requests.org/。

# # -*- coding: utf-8 -*- # 数据爬取文件 import scrapy import pymysql import pymssql from ..items import ErshoufanginfoItem import time from datetime import datetime,timedelta import datetime as formattime import re import random import platform import json import os import urllib from urllib.parse import urlparse import requests import emoji import numpy as np from DrissionPage import Chromium import pandas as pd from sqlalchemy import create_engine from selenium.webdriver import ChromeOptions, ActionChains from scrapy.http import TextResponse from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support.wait import WebDriverWait import pandas as pd from sqlalchemy import create_engine from selenium.webdriver import ChromeOptions, ActionChains from scrapy.http import TextResponse from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support.wait import WebDriverWait # 二手房信息 class ErshoufanginfoSpider(scrapy.Spider): name = 'ershoufanginfoSpider' spiderUrl = 'https://gy.anjuke.com/sale/p{}/' start_urls = spiderUrl.split(";") protocol = '' hostname = '' realtime = False def __init__(self,realtime=False,*args, **kwargs): super().__init__(*args, **kwargs) self.realtime = realtime=='true' def start_requests(self): plat = platform.system().lower() if not self.realtime and (plat == 'linux' or plat == 'windows'): connect = self.db_connect() cursor = connect.cursor() if self.table_exists(cursor, 's6036zs0_ershoufanginfo') == 1: cursor.close() connect.close() self.temp_data() return pageNum = 1 + 1 for url in self.start_urls: if '{}' in url: for page in range(1, pageNum): next_lin是用什么解析
最新发布
03-25
<think>嗯,用户让我分析这段Python爬虫代码,特别是询问用的是哪种解析方法。首先,我需要仔细看一下用户提供的代码。代码开头有很多导入的库,比如scrapy、pymysql、pymssql,还有从items模块导入的ErshoufanginfoItem,还有一些处理时间和数据的库,比如datetime、re、random等。接着是各种浏览器驱动相关的导入,比如Chromium、ChromeOptions、ActionChains,以及selenium的相关模块,比如WebDriverWait、By、EC。这说明这个爬虫可能结合了Scrapy框架和Selenium来进行网页内容的获取和解析。 然后看用户定义的爬虫类ErshoufanginfoSpider,继承自scrapy.Spider。name是'ershoufanginfoSpider',start_urls是构造的URL,看起来是安居客的二手房信息页面。在start_requests方法里,用户可能首先生成需要爬取的页面链接,然后发送请求。不过代码中的start_requests方法似乎没有完成,最后的循环部分被截断了,用户可能没粘贴完整。比如,在for page in range(1, pageNum)之后,应该是构造具体的URL,然后通过yield scrapy.Request发送请求。但是用户提供的代码到这里就中断了,可能是在粘贴的时候出了问题。 接下来,关于解析方法,通常在Scrapy中,我们会定义parse方法来处理响应。但用户提供的代码中没有展示parse方法的具体实现。不过,从导入的模块来看,可能使用了两种解析方式:一种是Scrapy自带的Selector基于XPath或CSS选择器,另一种是结合Selenium的WebDriver来渲染页面并提取数据。此外,代码中使用了DrissionPage的Chromium,这可能是一种更高级的浏览器自动化工具,结合了requests和selenium的优点。 在用户代码中,还看到使用了sqlalchemy的create_engine,说明数据存储可能用到了ORM框架,结合pymysql或pymssql连接数据库。而items.py中的ErshoufanginfoItem可能定义了爬取字段的结构,用于存储二手房信息。 关于解析方法的具体判断,由于代码中的解析逻辑没有展示出来,需要根据导入的库和常见做法推断。Scrapy通常使用Response对象的xpath()或css()方法进行解析,而如果页面是动态渲染的,可能会配合Selenium或DrissionPage来获取渲染后的页面内容,再用XPath或正则表达式提取数据。比如,可能用Selenium的WebDriverWait等待某些元素加载完成,然后通过find_element(By.XPATH, ...)获取元素,接着提取文本或属性。 用户代码中导入了TextResponse,这可能意味着在某些情况下,将Selenium获取的页面内容转换成Scrapy的Response对象,进而使用Scrapy的解析方法。例如,用Selenium获取页面源代码,然后构造TextResponse对象,这样就可以用Scrapy的Selector来解析,这样结合了两者的优势。 总结来说,可能的解析方法包括: 1. 使用Scrapy内置的Selector(XPath/CSS)解析静态HTML。 2. 结合Selenium或DrissionPage处理JavaScript动态加载的内容,再使用XPath或CSS选择器提取数据。 3. 可能用正则表达式(re模块)处理复杂的文本匹配。 4. 使用pandas和numpy进行数据清洗和整理,但这不是解析阶段的主要任务。 不过,由于用户提供的代码不完整,特别是缺少parse方法的实现,无法确切知道具体用了哪种解析方式。但根据导入的模块和常见模式,推测主要使用Scrapy的Selector结合XPath/CSS,同时可能需要Selenium处理动态内容,特别是在安居客这类可能反爬较强的网站上。此外,DrissionPage的Chromium可能用于更高效的页面渲染和控制,替代统的Selenium WebDriver。</think>这段代码是一个基于Scrapy框架的二手房信息爬虫,结合了多种解析技术。以下是对其解析方式的逐步分析: 1. **基础架构解析**: - 核心框架:`scrapy.Spider`(代码第34行) - 使用Scrapy的内建请求处理机制(`start_requests`方法) - 典型爬虫结构:定义`name`、`start_urls`等基础属性 2. **混合解析技术**: - **动态渲染支持**: ```python from DrissionPage import Chromium # 第25行 from selenium import webdriver # 第30行 ``` - 使用`DrissionPage`(融合requests和selenium的库) - 统Selenium的`WebDriver`支持(代码30-33行) - 用于处理JavaScript动态加载内容 - **静态解析能力**: ```python from scrapy.http import TextResponse # 第29行 ``` - 可将动态渲染后的页面转换为Scrapy的Response对象 - 支持后续使用XPath/CSS选择器解析 3. **典型解析流程**(代码逻辑推断): ```python # 伪代码逻辑(实际实现应包含在parse方法中) def parse(self, response): # 使用DrissionPage/Selenium获取动态内容 with Chromium() as driver: driver.get(response.url) rendered_html = driver.html # 转换为Scrapy响应对象 dynamic_response = TextResponse(url=response.url, body=rendered_html, encoding='utf-8') # 使用XPath提取数据 items = dynamic_response.xpath('//div[@class="property-item"]') for item in items: yield { "title": item.xpath('./h3/text()').get(), "price": item.xpath('.//span[@class="price"]/text()').get() } ``` 4. **技术组合优势**: - `Scrapy`:提供高效异步爬取框架 - `Selenium/DrissionPage`:解决反爬和动态渲染问题 - `XPath/CSS`:保持解析代码的简洁性 - `TextResponse`:实现动态内容与统解析的无缝衔接 5. **特殊数据处理**: ```python import re # 第10行(正则表达式) import emoji # 第18行(表情符号处理) ``` - 使用正则表达式处理复杂文本模式 - 专门的表情符号处理模块,应对特殊字符清洗 建议优化方向:可以添加`scrapy-selenium`中间件实现更规范的动态渲染集成,同时建议使用`parsel`库统一XPath/CSS解析语法,提升代码可维护性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值