跳到主要内容 Python 网络爬虫技术基础与实战 | 极客日志
Python 大前端 算法
Python 网络爬虫技术基础与实战 本文介绍了 Python 网络爬虫的基础知识与核心技术。涵盖 requests 库的使用,包括 GET/POST 请求、Headers 伪装、Cookie 管理及代理设置。详细讲解了三种主流数据解析方案:正则表达式(re)、BeautifulSoup 及 XPath(lxml),并对比了 pyquery 的 CSS 选择器用法。此外,文章还探讨了提升爬虫效率的并发编程手段,包括多线程、多进程、线程池以及基于 asyncio 的协程与 aiohttp 异步框架,帮助开发者构建高效稳定的数据采集系统。
二、基础
(一)获取网页源代码 from urllib.request import urlopen
url = 'http://www.baidu.com'
response = urlopen(url)
print (response.read().decode('utf-8' ))
(二)网页加载方式
静态页面,全部加载;
动态网页,数据和页面分开加载和请求。
三、requests 模块
(一)安装
(二)使用 import requests
url = "http://www.baidu.com"
response = requests.get(url)
response.encoding = "utf-8"
print (response.text)
(三)变量访问与伪装 import requests
content = input ("请输入要搜索的内容:" )
url = f"http://www.baidu.com/s?wd={content} "
headers = {"User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36" }
response = requests.get(url, headers=headers)
print (response.text)
使用 headers 自定义请求头部分,修改 UA 以模仿浏览器进行访问。
(四)post 请求获取翻译信息 import requests
url = "https://fanyi.baidu.com/sug"
data = {"kw" : input ("请输入要翻译的单词:" )}
response = requests.post(url, data=data)
print (response.json())
post 在 data 段包含发送的数据,一般返回 json 格式的数据。
(五)get 请求获取信息 import requests
url = "https://movie.douban.com/j/chart/top_list"
data = {"type" : 5 , "interval_id" : "100:90" , "action" : "" , "start" : 0 , "limit" : 20 }
headers = {"User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.64 Safari/537.36" }
response = requests.get(url, params=data, headers=headers)
print (response.json())
get 请求时会将 data 段自动拼接到 url 后面。
(六)cookie 处理 import requests
session = requests.Session()
url = "http://www.baidu.com"
data = {"username" : "username" , "password" : "password" }
response = session.post(url, data=data)
print (response.text)
创建一个 Session 对象,可以在同一个 Session 对象中保持 cookie 等信息。除了 session 还可以在 headers 直接写入 cookie 字段。
(七)防盗链 在 headers 中加入 refer,规避溯源反扒机制。
(八)代理使用 import requests
url = "http://www.baidu.com"
proxy = {"http" : "http://127.0.0.1:10809" , "https" : "http://127.0.0.1:10809" }
r = requests.get(url, proxies=proxy)
print (r.text)
使用代理可以减少服务器封 ip 地址的情况,但速度较慢,费用较贵。
四、数据处理
(一)正则表达式
1. 元字符 符号 含义 . 匹配除换行符以外的任意字符 \w 字母、数字、下划线 \s 空白符 \d 数字 \n 换行符 \t 制表符 ^ 字符串开头 $ 字符串结尾 \W 非字母、非数字、非下划线 \D 非数字 \S 非空白符 a b () 分组,匹配括号内表达式 [...] 匹配字符串中的字符,如 a-z [^...] 匹配除字符串中字符的所有字符
2. 量词 符号 含义 * 重复 0 次或更多次数 + 重复 1 次或更多次数 ? 重复 0 或 1 次 {n} 重复 n 次 {n,} 重复 n 次或更多次数 {n,m} 重复 n 到 m 次 .* 贪婪匹配,尽可能多的匹配 .*? 惰性匹配,尽可能少的匹配
3. 使用:re 库 import re
text = "我的电话号码是 1234567890 和 1234567891,请不要告诉别人。"
pattern = r"\d{10}"
result = re.findall(pattern, text)
print (result)
import re
text = "我的电话号码是 1234567890 和 1234567891,请不要告诉别人。"
pattern = r"\d{10}"
result = re.finditer(pattern, text)
for match in result:
print (match .group())
import re
text = "我的电话号码是 1234567890 和 1234567891,请不要告诉别人。"
pattern = r"\d{10}"
result = re.search(pattern, text)
print (result.group())
从字符串开头开始匹配 (如果第一个字符不符合则失败)
import re
text = "我的电话号码是 1234567890 和 1234567891,请不要告诉别人。"
pattern = r"\d{10}"
result = re.match (pattern, text)
print (result)
import re
pattern_obj = re.compile (r"\d{10}" )
text = "我的电话号码是 1234567890 和 1234567891,请不要告诉别人。"
result1 = pattern_obj.findall(text)
result2 = pattern_obj.finditer(text)
result3 = pattern_obj.search(text)
print (result1)
print (list (result2))
print (result3.group())
import re
pattern_obj = re.compile (r'<div class="(?P<class>.*?)">(?P<content>.*?)</div>' )
s = """ <div>这是一个标题</div>
<div>这是一个内容</div> """
result = pattern_obj.finditer(s)
for item in result:
class_name = item.group("class" )
content = item.group("content" )
print (f"类名:{class_name} ,内容:{content} " )
(二)使用 BeautifulSoup 处理 from bs4 import BeautifulSoup
html = """ <div>这是一个标题</div>
<div>这是一个内容</div> """
page = BeautifulSoup(html, "html.parser" )
div = page.find("div" , class_="title" )
print (div.text)
divs = page.find_all("div" )
classes = page.find_all("div" , class_=True )
print (classes)
for div in divs:
print (div.text)
print (div.get("class" ))
find只找一个符合的。
文本用 .text,图片等用 .img 或 .src。
(三)xpath 处理 (lxml) from lxml import etree
xml = """ <books>
<id>1</id>
<author>
<nickname>Neo</nickname>
<nickname>小王子</nickname>
<nickname>Python 爬虫基础</nickname>
</author>
<price>100</price>
<name>Python 爬虫基础</name>
</books> """
tree = etree.XML(xml)
et = tree.xpath('//nickname/text()' )
result = tree.xpath('//nickname' )
for item in result:
print (item.text)
one = tree.xpath('//nickname[@class="1"]/text()' )[0 ]
text() 获取节点内容,默认是列表,如果取出第一个元素,可以使用 [0];//表示所有节点,/表示根节点;@class="1" 获取属性为 class 的值为 1 的节点内容。
(四)pyquery from pyquery import PyQuery as pq
html = """ <div>
<ul>
<li>first item</li>
<li><a href="link2.html">second item</a></li>
<li><a href="link3.html"><span>third item</span></a></li>
<li><a href="link4.html">fourth item</a></li>
<li><a href="link5.html">fifth item</a></li>
</ul>
</div> """
doc = pq(html)
a = doc("li" )
b = doc("li a" )
c = doc("li a span" )
class_names = doc("li" ).attr("class" )
item_text = doc("li" ).text()
print (class_names)
print (item_text)
print (a)
print (b)
print (c)
attr("class") 获取第一个 li 标签的 class 属性值,多个标签会返回第一个标签的属性值,如果想获取所有标签的属性值,可以使用 items() 方法;text() 获取所有 li 标签的文本内容,多个标签会返回所有标签的文本内容;pyquery 中就是直接用 css 选择器,注意返回的不是字符串,而是 pyquery 对象。
from pyquery import PyQuery as pq
html = """ <div>这是一个标题</div>
<div>这是一个内容</div> """
doc = pq(html)
after = doc(".title" ).after("<div>这是一个 after 标签</div>" )
before = doc(".title" ).before("<div>这是一个 before 标签</div>" )
print (doc)
remove = doc(".title" ).remove()
remove_attr = doc(".content" ).remove_attr("class" )
print (doc)
add_class = doc(".after" ).add_class("new-class" )
add_attr = doc(".after" ).attr("data-id" , "123" )
print (doc)
after()、before() 在.title 标签后面、前面添加一个新的标签;remove() 删除标签;remove_attr("class") 删除标签的 class 属性;add_class("new-class") 给标签添加一个新的 class 属性值;attr("data-id", "123") 给标签添加一个新的属性 data-id,值为 123,如果属性已经存在,会覆盖原有的属性值。
五、提高效率
(一)多线程 from threading import Thread
def task ():
for i in range (5 ):
print (f"线程任务执行第 {i+1 } 次" )
thread = Thread(target=task)
thread.start()
for i in range (5 ):
print (f"主线程执行第 {i+1 } 次" )
通过 Thread 传递参数给函数时使用 args=() 括号内必须为元组,如果只有一个变量,要在后面加逗号。
(二)多进程 from multiprocessing import Process
def task ():
for i in range (5 ):
print (f"子进程任务执行第 {i+1 } 次" )
pro = Process(target=task)
pro.start()
for i in range (5 ):
print (f"主进程执行第 {i+1 } 次" )
(三)线程池 from concurrent.futures import ThreadPoolExecutor
def task (n ):
print (f"线程任务执行第 {n} 次" )
with ThreadPoolExecutor(max_workers=5 ) as executor:
for i in range (40 ):
executor.submit(task, i+1 )
print (f"主线程提交了第 {i+1 } 个任务" )
(四)协程 import asyncio
async def task (n ):
for i in range (10 ):
print (f"异步任务执行第 {n} 次,第 {i+1 } 次" )
await asyncio.sleep(1 )
async def task2 (n ):
for i in range (10 ):
print (f"异步任务 2 执行第 {n} 次,第 {i+1 } 次" )
await asyncio.sleep(1 )
async def task3 (n ):
for i in range (10 ):
print (f"异步任务 3 执行第 {n} 次,第 {i+1 } 次" )
await asyncio.sleep(1 )
await asyncio.gather(task(1 ), task2(2 ), task3(3 ))
import asyncio
async def task (n ):
for i in range (10 ):
print (f"异步任务执行第 {n} 次,第 {i+1 } 次" )
await asyncio.sleep(1 )
async def task2 (n ):
for i in range (10 ):
print (f"异步任务 2 执行第 {n} 次,第 {i+1 } 次" )
await asyncio.sleep(1 )
async def task3 (n ):
for i in range (10 ):
print (f"异步任务 3 执行第 {n} 次,第 {i+1 } 次" )
await asyncio.sleep(1 )
async def main ():
tasks = [asyncio.create_task(task(1 )), asyncio.create_task(task2(2 )), asyncio.create_task(task3(3 ))]
await asyncio.wait(tasks)
if __name__ == "__main__" :
asyncio.run(main())
aiohttp import asyncio
import aiohttp
async def fetch (url ):
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
return await response.text()
async def main ():
url = "http://www.baidu.com"
content = await fetch(url)
print (content)
if __name__ == "__main__" :
asyncio.run(main())
微信扫一扫,关注极客日志 微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
相关免费在线工具 加密/解密文本 使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
curl 转代码 解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online
Base64 字符串编码/解码 将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
Base64 文件转换器 将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
Markdown转HTML 将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
HTML转Markdown 将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online