Python3网络爬虫开发实战(2)爬虫基础库
创始人
2024-11-18 20:05:18
0

文章目录

  • 一、urllib
    • 1. urlparse 实现 URL 的识别和分段
    • 2. urlunparse 用于构造 URL
    • 3. urljoin 用于两个链接的拼接
    • 4. urlencode 将 params 字典序列化为 params 字符串
    • 5. parse_qs 和 parse_qsl 用于将 params 字符串反序列化为 params 字典或列表
    • 6. quote 和 unquote 对 URL的中文字符进行编码和解码
  • 二、requests
    • 1. GET 请求
    • 2. POST 请求
    • 3. Session 维持
    • 4. 响应
    • 5. 身份认证
    • 6. 设置代理
    • 7. Prepared Request
  • 三、httpx
    • 1. Requests Compatibility
    • 2. 异步
  • 四、基础爬虫实战

一、urllib

urllib 类似于 python 底层构建请求,构建相对于其他的库来说较为复杂,不过 urllib 解析链接非常好用

1. urlparse 实现 URL 的识别和分段

如果需要合并 params 和 query 可以使用 urlsplit

from urllib.parse import urlparse  urlparse('https://www.baidu.com/')  # ParseResult(scheme='https', netloc='[www.baidu.com](https://www.baidu.com/)', path='/', params='', query='', fragment='') 

2. urlunparse 用于构造 URL

urlunparse 这个方法接收的参数是一个可迭代对象,且其长度必须为 6;同样的,如果需要合并 params 和 query 可以使用 urlunsplit

from urllib.parse import urlunparse  data = ['https','www.baidu.com','index.html','user','a=6','comment']  urlunparse(data) # https://www.baidu.com/index.html;user?a=6#comment 

3. urljoin 用于两个链接的拼接

urljoin 首先会解析 new_url,判断其 scheme,netloc,path 是否出现了缺失,如果确实使用 base_url 中的 scheme,netloc,path 对应缺失部分代替;

from urllib.parse import urljoin  base_url = 'https://www.baidu.com' new_url = 'FAQ.html'  urljoin(base_url, new_url) # https://www.baidu.com/FAQ.html 

4. urlencode 将 params 字典序列化为 params 字符串

from urllib.parse import urlencode  params = {     'name': 'germey',     'age': 2, }  base_url = 'https://www.baidu.com?' base_url + urlencode(params) # https://www.baidu.com?name=germey&age=2 

5. parse_qs 和 parse_qsl 用于将 params 字符串反序列化为 params 字典或列表

from urllib.parse import parse_qs, parse_qsl  params = 'name=germey&age=25'  parse_qs(params, separator='&') # {'name': ['germey'], 'age': ['25']}  parse_qsl(params, separator='&') [('name', 'germey'), ('age', '25')] 

6. quote 和 unquote 对 URL的中文字符进行编码和解码

from urllib.parse import quote, unquote  url = "https://www.baidu.com/s?wd=爬虫"  # utf8编码,指定安全字符 quote(url, safe=";/?:@&=+$,", encoding="utf-8") # https://www.baidu.com/s?wd=%E7%88%AC%E8%99%AB  # gbk编码,指定安全字符 quote(url, safe=";/?:@&=+$,", encoding="gbk") # https://www.baidu.com/s?wd=%C5%C0%B3%E6  # utf8解码 unquote('https://www.baidu.com/s?wd=%E7%88%AC%E8%99%AB', encoding='utf-8') # https://www.baidu.com/s?wd=爬虫  # gbk解码 unquote('https://www.baidu.com/s?wd=%C5%C0%B3%E6', encoding='gbk') # https://www.baidu.com/s?wd=爬虫 

二、requests

1. GET 请求

测试 URL: www.httpbin.org/get

cookies 可以单独设置,也可以放在 headers 的 cookie 字段下传入请求之中,timeout 可以控制超时时间,headers 是请求头,params 是参数构建完整的 url;

import requests  params = {} headers = {} cookies = {}  # vertify 设置为 False 可以避免 ssl 认证 requests.get(url=url, params=params, headers=headers, cookies=cookies, vertify=False, timeout=None)  

2. POST 请求

测试 URL: www.httpbin.org/post

POST 是上传东西的常用请求,POST 请求中除了 GET 请求中的那些参数,还有一些参数可以使用,如 data 和 file;其中 data 主要用来传表单,而 file 主要用来传文件;

import requests  params = {} headers = {} cookies = {}  data = {} file = {}  # vertify 设置为 False 可以避免 ssl 认证 requests.post(url=url, params=params, headers=headers, cookies=cookies, vertify=False, timeout=None, data=data, file=file) 

3. Session 维持

多次直接利用 requests 库中的 get 或 post 方法模拟网络请求,相当于打开了多个不同的浏览器,而使用 Session 维持 搭配 get 和 post 方法去模拟网络请求,相当于打开了一个浏览器中的多个页面;

import requests  s = requests.Session(headers=headers) s.get(url_1) s.get(url_2)  # 这里在第一次 get 请求中获得到的 cookie 就会保持进而在第二次 get 请求中得到 

4. 响应

import requests  resp = requests.get()  # 状态码 resp.status_code  # 响应头 resp.headers  # cookies resp.cookies  # 最终 url 搭配重定向使用 requests.get(url, allow_redirects=False) resp.url  # 请求历史 resp.history  # 在获取 resp.text 先配置 encoding resp.encoding  # 响应结果字符串形式,需要搭配 resp.encoding = 'utf-8' or 'gbk' 使用 resp.text  # 二进制相应结果,通常对应于文件 resp.content  # resp.text 转化为 json 数据,如果不是json 数据格式,则会出现解析错误,抛出 json.decoder.JSONDecodeError 异常 resp.json 

5. 身份认证

在访问启用了基本身份认证的网站时,首先会弹出一个认证窗口,认证正确会弹出 200状态码,如果认证错误或者不进行认证会弹出 401 错误;

import requests from requests.auth import HTTPBasicAuth  # 第一行是第二行的简写 r = requests.get('https://ssr3.scrape.center/',auth=('admin','admin')) r = requests.get('https://ssr3.scrape.center/',auth=HTTPBasicAuth('admin','admin'))  r.status_code # 200 

requests 库还提供了其他的认证方式,如 OAuth 认证,需要安装 oauth 包;

6. 设置代理

首先是基本的 HTTP 代理

import requests  proxies = {     'http':'http://10.10.10,10:1080',     'https':'https://10.10.10.10:1080', }  requests.get('https://www.httpbin.org/get', proxies=proxies) 

除了基本的 HTTP 代理外,还支持 SOCKS 协议的代理,首先需要安装 socks 库 pip install requests[socks]

import requests  proxies = {     'http':'socks5://10.10.10,10:1080',     'https':'socks5://10.10.10.10:1080', }  requests.get('https://www.httpbin.org/get', proxies=proxies) 

7. Prepared Request

因此多个 get 或者 post 请求相当于多个 Session,尽量避免对同一网页使用多个 get 或者 post 请求

from requests import Request,Session  url = 'https://www.httpbin.org/post' data = {'name':'germey'} headers = {}  # 请求的底层 s = Session() req = Request('POST', url, data=data, headers=headers) prepped = s.prepare_request(req) r = s.send(prepped)  # 等价 r = requests.post(url, data=data, headers=headers) 

三、httpx

HTTPX 建立在 requests 完善的可用性之上,支持 HTTP/2 并支持异步;HTTPX (python-httpx.org)

可选安装如下:

  • h2 - HTTP/2 支持。 (可选,带有 httpx[http2]
  • socksio - SOCKS 代理支持。 (可选,带有 httpx[socks]
  • brotlibrotlicffi - 解码“brotli”压缩响应。 (可选,带有 httpx[brotli]

HTTPX 与 requests 的 API 广泛兼容,在少部分地方存在一些设计差异:Requests Compatibility - HTTPX (python-httpx.org)

1. Requests Compatibility

重定向:与 requests 不同,HTTPX 默认情况下是不遵循 重定向 (redirects) 的,开启重定向如下所示

client = httpx.Client(follow_redirects=True) response = client.get(url, follow_redirects=True) 

Client:等价于 requests.Session 维持,即等价

session = requests.Session(**kwargs) client = httpx.Client(**kwargs) 

URL:访问 response.url 将返回 url 实例,requests 返回的是字符串

重定向请求requests 库公开了一个属性 response.next ,该属性可用于获取下一个重定向请求。在 HTTPX 中,此属性被命名为 response.next_request

# requests session = requests.Session() request = requests.Request("GET", ...).prepare() while request is not None: 	response = session.send(request, allow_redirects=False) 	request = response.next   # httpx client = httpx.Client() request = client.build_request("GET", ...) while request is not None: 	response = client.send(request) 	request = response.next_request 

请求内容:对于上传原始文本或二进制内容,httpx 使用 content 参数,以便更好地将这种用法与上传表单数据的情况分开。使用 content=... 上传原始内容,并使用 data=... 发送表单数据;

httpx.post(..., content=b"Hello, world") httpx.post(..., data={"message": "Hello, world"}) 

上传文件:HTTPX 严格强制上传文件必须以二进制模式打开,以避免尝试上传以文本模式打开的文件可能导致的字符编码问题。

内容编码:HTTPX 使用 utf-8 来编码 str 请求正文。例如,当使用 content= 时,请求正文将在通过线路发送之前编码为 utf-8

Cookietrust_envverifycert 参数:如果使用客户端实例,应始终在客户端实例化时传递,而不是传递给请求方法。

2. 异步

requests 是不支持异步的,通常我们会使用 aiohttp 来进行异步操作,而 httpx 不仅支持同步还支持异步

import asyncio import httpx  async def main():     async with httpx.AsyncClient() as client:         response = await client.get('https://www.example.com/')         print(response)  asyncio.run(main()) 

四、基础爬虫实战

任务:

  1. 使用爬虫基本库爬取 https://ssr1.scrape.center/ 每一页的电影列表,顺着列表再爬取每个电影的详细页
  2. 使用正则表达式提取每部电影的名称,封面,类别,上映时间,剧情简介等内容;
  3. 使用多进程实现爬取的加速;

流程:

代码:

import os import re import httpx import json from multiprocessing import Pool from urllib.parse import urljoin  base_url = "https://ssr1.scrape.center"   def scrape_index(page):     """获得page的url"""     page_url = f"{base_url}/page/{page}"     return page_url   def scrape_list(html):     """获得列表的url"""     url_list = re.findall(r'', html)     url_list = [urljoin(base_url, item) for item in url_list]     return url_list   def scrape_detail(html):     """获得详细页信息"""     detail_dic = {}     detail_dic["名称"] = (         re.search(r'

(.*?)

', html, re.S).group(1) if re.search(r'

(.*?)

', html, re.S) else None ) detail_dic["封面"] = ( re.search( r'class="item.*?', html, re.S ).group(1) if re.search(r'class="item.*?', html, re.S) else None ) detail_dic["类别"] = re.findall( r"(.*?).*?", html, re.S ) detail_dic["上映时间"] = ( re.search(r".*?(\d{4}-\d{2}-\d{2}) 上映", html, re.S).group(1) if re.search(r".*?(\d{4}-\d{2}-\d{2}) 上映", html, re.S) else None ) detail_dic["剧情简介"] = ( re.search(r"剧情简介.*?(.*?)

", html, re.S).group(1).strip() if re.search(r"剧情简介.*?(.*?)

", html, re.S) else None ) return detail_dic def validateTitle(title): """命名规范性""" rstr = r"[\/\\\:\*\?\"\<\>\|]" # '/ \ : * ? " < > |' new_title = re.sub(rstr, "_", title) # 替换为下划线 return new_title def save_json(detail_dic): """保存数据到json文件夹下""" os.makedirs("./json", exist_ok=True) name = detail_dic["名称"] data_path = f"./json/{validateTitle(name)}.json" json.dump( detail_dic, open(data_path, "w", encoding="utf-8"), ensure_ascii=False, indent=2 ) def main(page): client = httpx.Client() page_url = scrape_index(page) resp_page = client.get(page_url).text url_list = scrape_list(resp_page) for detail_url in url_list: resp_detail = client.get(detail_url).text detail_dic = scrape_detail(resp_detail) save_json(detail_dic) if __name__ == "__main__": pool = Pool(10) pages = range(1, 10 + 1) pool.map(main, pages) pool.close() pool.join()

得到结果如下:

相关内容

热门资讯

【OpenCV C++20 学... 图片融合原理实现结果展示完整代码原理关于OpenCV的配置和基础用法,请参阅本专栏的其...
以下哪种海洋鱼类会伪装成石头捕... 石头鱼。这种鱼因其体色和形态与海底石头相似而得名,会伪装成石头等待猎物接近后捕猎。伪装成石头的海洋鱼...
UWA Gears正式上线,助... 亲爱的开发者朋友们, 我们非常激动地向大家宣布,UWA最新的无SDK性能...
ewebeditor是什么 ewebeditor是一个基于浏览器的在线HTML编辑器,具有所见即所得(WYSIWYG)的功能,支...
雷电云手机怎么改设备名称-修改... 在雷电云手机中,通常可以通过设置菜单找到设备名称选项进行修改。具体步骤可能因应用版本而异,但一般路径...
wps里怎么做ppt模板 在WPS中,您可以通过以下步骤创建PPT模板:,,1. 打开WPS演示软件。,2. 选择“新建幻灯片...
通信类IEEE会议——第四届通... [IEEE 独立出版,中山大学主办,往届均已见刊检索]第四届通信技术与信...
怎么查询华为手机购买日期查询 在华为手机的拨号界面,输入*#*#121314#*#*,然后点击“版本信息”,即可查看到购买日期。如...
STM32——GPIO(点亮L... 一、什么是GPIO?GPIO(通用输入输出接口)ÿ...
米侠浏览器的使用方法 米侠浏览器的使用方法很简单,首先下载安装米侠浏览器,打开后可以直接在搜索框输入网址或关键词进行搜索,...