跳到主要内容Python 爬虫实战:7 个经典案例与源码解析 | 极客日志Python算法
Python 爬虫实战:7 个经典案例与源码解析
Python 爬虫实战教程涵盖正则、XPath、BeautifulSoup、Selenium 等核心技术。通过爬取贴吧回复、小说章节、豆瓣电影、京东评论、知乎问答、微博内容及票房数据等七个案例,演示了静态页面解析、动态加载处理、多线程优化及数据可视化分析。内容包含完整代码实现与关键步骤说明,适合初学者入门参考。
本文分享 7 个 Python 爬虫小案例,涉及正则表达式、XPath、BeautifulSoup、Selenium 等核心技术。适合初学者参考学习,涵盖静态页面解析、动态加载处理、多线程优化及数据可视化分析。
1. 使用正则表达式爬取贴吧帖子回复内容
本案例演示如何使用 requests 库配合正则表达式(re)抓取百度贴吧特定帖子的回复数据,并保存为 CSV 文件。重点在于通过正则匹配 HTML 结构中的评论用户、时间和内容字段。
技术要点:
- 使用 requests 发送 HTTP 请求获取网页源码。
- 利用 re.findall 提取特定 class 或 style 包裹的文本。
- 使用 csv 模块将清洗后的数据写入本地文件。
- 添加延时避免触发反爬机制。
import csv
import requests
import re
import time
def main(page):
url = f'https://tieba.baidu.com/p/7882177660?pn={page}'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36'
}
resp = requests.get(url, headers=headers)
html = resp.text
comments = re.findall('style="display:;"> (.*?)</div>', html)
users = re.findall('class="p_author_name j_user_card" href=".*?" target="_blank">(.*?)</a>', html)
comment_times = re.findall('楼</span><span class="tail-info">(.*?)</span><div', html)
for u, c, t in zip(users, comments, comment_times):
if 'img' in c or 'div' in c or len(u) > 50:
continue
csvwriter.writerow((u, t, c))
print(u, t, c)
()
__name__ == :
(, , encoding=) f:
csvwriter = csv.writer(f)
csvwriter.writerow((, , ))
page (, ):
main(page)
time.sleep()
相关免费在线工具
- 加密/解密文本
使用加密算法(如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
print
f'第{page}页爬取完毕'
if
'__main__'
with
open
'01.csv'
'a'
'utf-8'
as
'评论用户'
'评论时间'
'评论内容'
for
in
range
1
8
2
2. 多线程小说章节爬取与数据库存储
本案例实现多线程并发爬取小说网站章节内容,并将数据存储到 MySQL 数据库中。展示了如何封装数据库连接、使用 XPath 解析链接以及利用 ThreadPoolExecutor 提高爬取效率。
- 使用 lxml.etree 进行高效的 HTML 节点定位。
- 封装 get_conn 和 close_conn 函数管理数据库连接生命周期。
- 使用 concurrent.futures.ThreadPoolExecutor 创建线程池。
- 对爬取内容进行清洗,去除广告和无关标签。
import requests
from lxml import etree
import re
import pymysql
from time import sleep
from concurrent.futures import ThreadPoolExecutor
def get_conn():
conn = pymysql.connect(host="127.0.0.1",
user="root",
password="root",
db="novels",
charset="utf8")
cursor = conn.cursor()
return conn, cursor
def close_conn(conn, cursor):
cursor.close()
conn.close()
def get_xpath_resp(url):
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36'}
resp = requests.get(url, headers=headers)
tree = etree.HTML(resp.text)
return tree, resp
def get_chapters(url):
tree, _ = get_xpath_resp(url)
novel_name = tree.xpath('//*[@id="info"]/h1/text()')[0]
dds = tree.xpath('/html/body/div[4]/dl/dd')
title_list = []
link_list = []
for d in dds[:15]:
title = d.xpath('./a/text()')[0]
title_list.append(title)
link = d.xpath('./a/@href')[0]
chapter_url = url + link
link_list.append(chapter_url)
return title_list, link_list, novel_name
def get_content(novel_name, title, url):
try:
cursor = None
conn = None
conn, cursor = get_conn()
sql = 'INSERT INTO novel(novel_name,chapter_name,content) VALUES(%s,%s,%s)'
tree, resp = get_xpath_resp(url)
content = re.findall('<div id="content">(.*?)</div>', resp.text)[0]
content = content.replace('<br />', '\n').replace(' ', ' ').replace('全本小说网 www.qb5.tw,最快更新<a href="https://www.qb5.tw/book_116659/">宇宙职业选手</a>最新章节!<br><br>', '')
print(title, content)
cursor.execute(sql, [novel_name, title, content])
conn.commit()
except:
pass
finally:
sleep(2)
close_conn(conn, cursor)
if __name__ == '__main__':
title_list, link_list, novel_name = get_chapters('https://www.qb5.tw/book_116659/')
with ThreadPoolExecutor(5) as t:
for title, link in zip(title_list, link_list):
t.submit(get_content, novel_name, title, link)
3. 豆瓣电影 Top250 数据爬取(XPath 与 BeautifulSoup 对比)
本案例分别使用 XPath 和 BeautifulSoup4 两种方式爬取豆瓣电影排行榜数据。通过对比两种解析库的使用方式,帮助读者理解不同工具的特点。数据包括电影名称、描述、评分和评价人数。
- XPath 版本:使用 lxml 库,路径定位精确,适合复杂结构。
- BeautifulSoup 版本:使用 bs4 库,API 简洁,适合快速开发。
- 两者均将结果保存至 CSV 文件以便后续分析。
import re
from time import sleep
import requests
from lxml import etree
import random
import csv
def main(page, f):
url = f'https://movie.douban.com/top250?start={page*25}&filter='
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.35 Safari/537.36',}
resp = requests.get(url, headers=headers)
tree = etree.HTML(resp.text)
href_list = tree.xpath('//*[@id="content"]/div/div[1]/ol/li/div/div[1]/a/@href')
name_list = tree.xpath('//*[@id="content"]/div/div[1]/ol/li/div/div[2]/div[1]/a/span[1]/text()')
for url_item, name in zip(href_list, name_list):
f.flush()
try:
get_info(url_item, name)
except:
pass
sleep(1 + random.random())
print(f'第{i+1}页爬取完毕')
def get_info(url, name):
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.35 Safari/537.36',
'Host': 'movie.douban.com',
}
resp = requests.get(url, headers=headers)
html = resp.text
tree = etree.HTML(html)
dir = tree.xpath('//*[@id="info"]/span[1]/span[2]/a/text()')[0]
type_ = re.findall(r'property="v:genre">(.*?)</span>', html)
type_ = '/'.join(type_)
country = re.findall(r'地区:</span> (.*?)<br', html)[0]
time = tree.xpath('//*[@id="content"]/h1/span[2]/text()')[0]
time = time[1:5]
rate = tree.xpath('//*[@id="interest_sectl"]/div[1]/div[2]/strong/text()')[0]
people = tree.xpath('//*[@id="interest_sectl"]/div[1]/div[2]/div/div[2]/a/span/text()')[0]
print(name, dir, type_, country, time, rate, people)
csvwriter.writerow((name, dir, type_, country, time, rate, people))
if __name__ == '__main__':
with open('03-movie-xpath.csv', 'a', encoding='utf-8', newline='') as f:
csvwriter = csv.writer(f)
csvwriter.writerow(('电影名称', '导演', '电影类型', '国家', '上映年份', '评分', '评论人数'))
for i in range(10):
main(i, f)
sleep(3 + random.random())
import random
import urllib.request
from bs4 import BeautifulSoup
import codecs
from time import sleep
def main(url, headers):
page = urllib.request.Request(url, headers=headers)
page = urllib.request.urlopen(page)
contents = page.read()
soup = BeautifulSoup(contents, "html.parser")
infofile.write("")
print('爬取豆瓣电影 250: \n')
for tag in soup.find_all(attrs={"class": "item"}):
num = tag.find('em').get_text()
print(num)
infofile.write(num + "\r\n")
name = tag.find_all(attrs={"class": "title"})
zwname = name[0].get_text()
print('[中文名称]', zwname)
infofile.write("[中文名称]" + zwname + "\r\n")
url_movie = tag.find(attrs={"class": "hd"}).a
urls = url_movie.attrs['href']
print('[网页链接]', urls)
infofile.write("[网页链接]" + urls + "\r\n")
info = tag.find(attrs={"class": "star"}).get_text()
info = info.replace('\n', ' ')
info = info.lstrip()
print('[评分评论]', info)
info = tag.find(attrs={"class": "inq"})
if (info):
content = info.get_text()
print('[影评]', content)
infofile.write(u"[影评]" + content + "\r\n")
print('')
if __name__ == '__main__':
infofile = codecs.open("03-movie-bs4.txt", 'a', 'utf-8')
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36'}
i = 0
while i < 10:
print('页码', (i + 1))
num = i * 25
url = 'https://movie.douban.com/top250?start=' + str(num) + '&filter='
main(url, headers)
sleep(5 + random.random())
infofile.write("\r\n\r\n")
i = i + 1
infofile.close()
4. 京东商品评论数据爬取
本案例针对动态加载的电商评论页面,通过抓包分析 API 接口参数,直接请求后端数据接口获取 JSON 格式评论数据。包含评论内容、时间和评分。
- 识别 Ajax 异步请求接口。
- 构造正确的 params 参数模拟分页。
- 解析 JSON 响应对象提取列表数据。
import requests
import csv
from time import sleep
import random
def main(page, f):
url = 'https://club.jd.com/comment/productPageComments.action'
params = {
'productId': 100011483893,
'score': 0,
'sortType': 5,
'page': page,
'pageSize': 10,
'isShadowSku': 0,
'fold': 1
}
headers = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.35 Safari/537.36',
'referer': 'https://item.jd.com/'
}
resp = requests.get(url, params=params, headers=headers).json()
comments = resp['comments']
for comment in comments:
content = comment['content']
content = content.replace('\n', '')
comment_time = comment['creationTime']
score = comment['score']
print(score, comment_time, content)
csvwriter.writerow((score, comment_time, content))
print(f'第{page+1}页爬取完毕')
if __name__ == '__main__':
with open('04.csv', 'a', encoding='utf-8', newline='') as f:
csvwriter = csv.writer(f)
csvwriter.writerow(('评分', '评论时间', '评论内容'))
for page in range(15):
main(page, f)
sleep(5 + random.random())
5. Selenium 模拟登录知乎并爬取问答
本案例演示如何使用 Selenium 自动化浏览器操作,模拟用户扫码登录知乎,搜索特定关键词并抓取问答详情及评论。适用于需要登录验证或 JavaScript 渲染复杂的场景。
- 配置 ChromeDriver 选项绕过检测。
- 使用 CDP 命令隐藏 webdriver 特征。
- 等待元素加载后执行点击和输入操作。
- 提取 DOM 节点文本作为数据源。
from time import sleep
from selenium.webdriver.chrome.service import Service
from selenium.webdriver import Chrome, ChromeOptions
from selenium.webdriver.common.by import By
import warnings
def main():
warnings.filterwarnings("ignore")
service = Service('chromedriver.exe')
options = ChromeOptions()
options.add_experimental_option('excludeSwitches', ['enable-automation', 'enable-logging'])
options.add_experimental_option('useAutomationExtension', False)
driver = Chrome(service=service, options=options)
driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
"source": """
Object.defineProperty(navigator, 'webdriver', {
get: () => false
})
"""
})
driver.get('https://www.zhihu.com/')
sleep(30)
driver.find_element(By.ID, 'Popover1-toggle').click()
driver.find_element(By.ID, 'Popover1-toggle').send_keys('汉江大学')
sleep(2)
driver.find_element(By.XPATH, '//*[@id="root"]/div/div[2]/header/div[2]/div[1]/div/form/div/div/label/button').click()
driver.implicitly_wait(20)
title = driver.find_element(By.XPATH, '//*[@id="SearchMain"]/div/div/div/div/div[2]/div/div/div/h2/div/a/span').text
driver.find_element(By.XPATH, '//*[@id="SearchMain"]/div/div/div/div/div[2]/div/div/div/div/span/div/button').click()
sleep(2)
content = driver.find_element(By.XPATH, '//*[@id="SearchMain"]/div/div/div/div/div[2]/div/div/div/div/span[1]/div/span/p').text
driver.find_element(By.XPATH, '//*[@id="SearchMain"]/div/div/div/div/div[2]/div/div/div/div/div[3]/div/div/button[1]').click()
sleep(2)
driver.find_element(By.XPATH, '//*[@id="SearchMain"]/div/div/div/div/div[2]/div/div/div/div[2]/div/div/div[2]/div[2]/div/div[3]/button').click()
sleep(2)
divs = driver.find_elements(By.XPATH, '/html/body/div[6]/div/div/div[2]/div/div/div/div[2]/div[3]/div')
try:
for div in divs:
comment = div.find_element(By.XPATH, './div/div/div[2]').text
f.write(comment)
f.write('\n')
print(comment)
except:
driver.close()
if __name__ == '__main__':
with open('05.txt', 'a', encoding='utf-8') as f:
main()
6. 微博用户历史博文爬取
本案例利用微博开放接口或内部 API,结合 Cookie 认证信息,批量爬取指定用户的历史微博内容。包括发布时间、作者、标题、转发数、评论数和点赞数。
- 获取有效的 Session Cookie 维持登录状态。
- 解析 AJAX 返回的 JSON 数据结构。
- 循环翻页获取多页数据。
import requests
import csv
from time import sleep
import random
def main(page):
url = f'https://weibo.com/ajax/statuses/mymblog?uid=2803301701&page={page}&feature=0&since_id=4824543023860882kp{page}'
headers = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36',
'cookie': 'SINAGLOBAL=6330339198688.262.1661412257300; ULV=1661412257303:1:1:1:6330339198688.262.1661412257300:; PC_TOKEN=8b935a3a6e; SUBP=0033WrSXqPxfM725Ws9jqgMF55529P9D9WWoQDW1G.Vsux_WIbm9NsCq5JpX5KMhUgL.FoMNShMN1K5ESKq2dJLoIpjLxKnL1h.LB.-LxKqLBoBLB.-LxKqLBKeLB--t; ALF=1697345086; SSOLoginState=1665809086; SCF=Auy-TaGDNaCT06C4RU3M3kQ0-QgmTXuo9D79pM7HVAjce1K3W92R1-fHAP3gXR6orrHK_FSwDsodoGTj7nX_1Hw.; SUB=_2A25OTkruDeRhGeFJ71UW-S7OzjqIHXVtOjsmrDV8PUNbmtANLVKmkW9Nf9yGtaKedmyOsDKGh84ivtfHMGwvRNtZ; XSRF-TOKEN=LK4bhZJ7sEohF6dtSwhZnTS4; WBPSESS=PfYjpkhjwcpEXrS7xtxJwmpyQoHWuGAMhQkKHvr_seQNjwPPx0HJgSgqWTZiNRgDxypgeqzSMsbVyaDvo7ng6uTdC9Brt07zYoh6wXXhQjMtzAXot-tZzLRlW_69Am82CXWOFfcvM4AzsWlAI-6ZNA=='
}
resp = requests.get(url, headers=headers)
data_list = resp.json()['data']['list']
for item in data_list:
created_time = item['created_at']
author = item['user']['screen_name']
title = item['text_raw']
reposts_count = item['reposts_count']
comments_count = item['comments_count']
attitudes_count = item['attitudes_count']
csvwriter.writerow((created_time, author, title, reposts_count, comments_count, attitudes_count))
print(created_time, author, title, reposts_count, comments_count, attitudes_count)
print(f'第{page}页爬取完毕')
if __name__ == '__main__':
with open('06-2.csv', 'a', encoding='utf-8', newline='') as f:
csvwriter = csv.writer(f)
csvwriter.writerow(('发布时间', '发布作者', '帖子标题', '转发数', '评论数', '点赞数'))
for page in range(1, 6):
main(page)
sleep(5 + random.random())
7. 票房数据爬取与可视化分析
本案例自选热点主题,爬取艺恩娱数票房榜数据,并使用 Pandas 和 Matplotlib 进行统计分析。展示如何从数据抓取过渡到数据分析,输出图表和统计结论。
- 使用 requests.post 提交表单数据获取接口。
- 利用 Pandas 进行数据清洗和分组聚合。
- 使用 Matplotlib 绘制饼图和折线图展示趋势。
import requests
import csv
import pandas as pd
import matplotlib.pyplot as plt
import warnings
warnings.filterwarnings('ignore')
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
def main():
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36',}
data = {
'r': '0.9936776079863086',
'top': '50',
'type': '0',
}
resp = requests.post('https://ys.endata.cn/enlib-api/api/home/getrank_mainland.do', headers=headers, data=data)
data_list = resp.json()['data']['table0']
for item in data_list:
rank = item['Irank']
MovieName = item['MovieName']
ReleaseTime = item['ReleaseTime']
TotalPrice = item['BoxOffice']
AvgPrice = item['AvgBoxOffice']
AvgAudienceCount = item['AvgAudienceCount']
csvwriter.writerow((rank, MovieName, ReleaseTime, TotalPrice, AvgPrice, AvgAudienceCount))
print(rank, MovieName, ReleaseTime, TotalPrice, AvgPrice, AvgAudienceCount)
def data_analyze():
data = pd.read_csv('07.csv')
data['年份'] = data['上映时间'].apply(lambda x: x.split('-')[0])
df1 = data.groupby('年份')['总票房 (万)'].sum()
plt.figure(figsize=(6, 6))
plt.pie(df1, labels=df1.index.to_list(), autopct='%1.2f%%')
plt.title('各年度上榜电影总票房占比')
plt.show()
df1 = data.groupby('年份')['总票房 (万)'].sum()
plt.figure(figsize=(6, 6))
plt.plot(df1.index.to_list(), df1.values.tolist())
plt.title('各年度上榜电影总票房趋势')
plt.show()
print(data.sort_values(by='平均票价', ascending=False)[['年份', '电影名称', '平均票价']].head(10))
print(data.sort_values(by='平均场次', ascending=False)[['年份', '电影名称', '平均场次']].head(10))
if __name__ == '__main__':
with open('07.csv', 'w', encoding='utf-8', newline='') as f:
csvwriter = csv.writer(f)
csvwriter.writerow(('排名', '电影名称', '上映时间', '总票房 (万)', '平均票价', '平均场次'))
main()
data_analyze()
结语:
以上案例涵盖了 Python 爬虫的主要应用场景和技术栈。在实际开发中,请严格遵守目标网站的 robots 协议及相关法律法规,尊重版权与隐私,合理控制爬取频率,避免对服务器造成压力。建议将爬虫技术用于合法的数据收集与分析目的。