Python 爬虫
爬虫是什么?
我所理解的爬虫,本质是针对网络中直接对外呈现的内容,梳理其数据传输链路与展示规律,进而解析提取其中核心价值数据的技术手段。
例子一:
- 若我想快速掌握小红书每日的内容推送逻辑,可先查看其首页 —— 内容以列表形式呈现,每个卡片对应一条笔记内容。
- 我可以通过爬虫获取每个卡片的核心信息(如标题 Title 和封面图 Cover Image),再结合大数据分析、深度学习等方法,挖掘这些内容的价值,或是反推自己在小红书平台的用户画像特征。
例子二:
- 有时会遇到这类情况:某个网站的界面(UI)设计不符合使用习惯,但其承载的数据却无可替代;同时该网站未开放 API 接口,无法通过正规渠道获取数据源,也就无法自行搭建 Web 前端或移动应用,来更高效、直观地分析数据并辅助决策。
- 这时就可以借助爬虫工具,在合规前提下抓取网站前端呈现的结构化、有规律的数据,并将其存储到本地介质(如文件、SQLite 数据库等)。
- 后续可将这些数据接入自研的 Web 项目中(既可以是 Python Web + Jinja2 这类前后端不分离的架构,也可以基于 Spring Boot 封装为 Restful API,再对接 Vue/React 前端展示 —— 具体技术选型可灵活调整,并非核心)。
- 结合自定义的 UI 设计,能大幅提升大脑对数据的分析效率,辅助做出更精准的决策。
那么针对这个工具,就是我们要使用的爬虫了。
为什么用Python
- 从本质上看,爬虫是通过自动化方式模拟用户访问网站 / 移动应用,再按照预设的规则提取指定内容的程序(这是爬虫的基础定义;更前沿的爬虫会结合深度学习实现数据的自主学习与提取,这部分暂不展开)。
- 从技术实现角度,任何编程语言都能完成爬虫开发,比如 Java、JavaScript 等均具备相应能力。
- 但实际应用中存在关键差异:Java、JavaScript 等语言的爬虫生态,没有 Python 这么完善。Python 拥有 Beautiful Soup 这类功能成熟、易用性极高的爬虫专用库,能大幅降低开发成本。
- 更重要的是,Python 语法简洁易懂:同样的功能,Java 可能需要数十行代码实现,而 Python 调用第三方库往往几行就能完成。且爬虫任务大多属于脚本级需求,而非大型工程化开发 —— 它通常无需搭建庞大的服务集群,对工程化规范的要求相对宽松,往往一段 50 行以内的代码就能完成完整的爬取、解析、存储流程。
- 因此,尽管爬虫的实现不局限于 Python,但凭借生态优势和开发效率,Python 成为了业界实现爬虫任务的主流选择。
Python 爬虫经常使用的几个核心工具
| Tools | Effected | Scene |
|---|---|---|
| requests | 发起 HTTP/HTTPS 请求 | 爬取静态网页、接口数据 |
| BeautifulSoup4 | 解析 HTML/XML 提取数据 | 静态网页数据解析 |
| lxml | 高性能 HTML/XML 解析 | 复杂网页解析 |
| Scrapy | 爬虫框架(整合请求 / 解析 / 存储) | 大规模、结构化爬取 |
| Selenium/Playwright | 模拟浏览器行为 | 爬取动态渲染网页 |
- 基础的爬虫步骤,就是可以通过requests实现一个网络请求的内容。
- 这里就需要注意一下网站方可能会做的一些tricks,比如你缺少Cookie、Content-Type、Auth等等内容,它会返回错误的状态给你,那么就需要去真实请求中,拿到这些数据,再通过Python模拟请求发送出去
- 比如可以通过Edge、Chrome这些浏览器,去请求网页,然后通过F12查看每一个请求携带的Header,找到欠缺的内容,然后在Python上通过Header传递上去。
- 拿到数据之后:
- 如果是Restful API的内容,我们会得到一个JSON内容(通常这类的数据,就是交给分离式的前端项目去fetch,或者交给移动端通过Retrofit2去解析)
- 而如果是直接呈现给用户观看到的Web内容,大抵就是一个html内容。这个时候,就可以去分析得到的这个HTML网页,如何按照一个规律性的角度,把核心的数据,拿出来,存起来,然后进一步去分析数据背后的商业逻辑。
实际案例
- 最近在家很无聊,合法的东西,我都不是很感兴趣。
- 黄赌毒里面,毒品我肯定是不敢碰的。
- 赌博这个角度的话,我最近在做另外一个项目,使用Python Web + Bootstrap搭建一个德州扑克和一个国际象棋的单机游戏。自己在家里玩单机版本的棋牌,挺有意思的。
- 所以对于爬虫这一块来讲,留下的不合法空间,就只有黄色了。
- 于是,我在想,要不使用爬虫去爬黄色网站的信息,然后把它封面图下载下来。
注意:实操此案例,需要保证网络可以访问Google,可以采用ShadowsRock、V2RayNG、ClashX等等方案,也可以参考路由器翻墙方案
我所选择要去爬取资源的网站是:JavBus
因为他们的url格式,真的很让人绷不住的简单,他们的格式就是:
- https://www.javbus.com/KBI-001
- 这种格式,就是他们的域名,然后加上番号,就是一个资源链接了。
它的Cover Image的区域,是在:
html
<div class="col-md-9 screencap">
<a class="bigImage" href="/pics/cover/6qah_b.jpg">
<img src="/pics/cover/6qah_b.jpg" title="KANBi専属出演第1弾!旦那を忘れる程 汗だく汁だくで絡み合う 濃厚接吻性交3本番 舌を絡ませ抱き合いながら絶頂に達する密着性交! 織笠るみ">
</a>
</div>- 而针对他们的样品图片这个区域,则是:
html
<div id="sample-waterfall">
<a class="sample-box" href="https://pics.dmm.co.jp/digital/video/118kbi00002/118kbi00002jp-1.jpg">
<div class="photo-frame">
<img src="/pics/sample/6qah_1.jpg" title="KBI-002 KANBi専属出演第1弾!旦那を忘れる程 汗だく汁だくで絡み合う 濃厚接吻性交3本番 舌を絡ませ抱き合いながら絶頂に達する密着性交! 織笠るみ - 樣品圖像 - 1">
</div>
</a>
<a class="sample-box" href="https://pics.dmm.co.jp/digital/video/118kbi00002/118kbi00002jp-2.jpg">
<div class="photo-frame">
<img src="/pics/sample/6qah_2.jpg" title="KBI-002 KANBi専属出演第1弾!旦那を忘れる程 汗だく汁だくで絡み合う 濃厚接吻性交3本番 舌を絡ませ抱き合いながら絶頂に達する密着性交! 織笠るみ - 樣品圖像 - 2">
</div>
</a>
<a class="sample-box" href="https://pics.dmm.co.jp/digital/video/118kbi00002/118kbi00002jp-3.jpg">
<div class="photo-frame">
<img src="/pics/sample/6qah_3.jpg" title="KBI-002 KANBi専属出演第1弾!旦那を忘れる程 汗だく汁だくで絡み合う 濃厚接吻性交3本番 舌を絡ませ抱き合いながら絶頂に達する密着性交! 織笠るみ - 樣品圖像 - 3">
</div>
</a>
<a class="sample-box" href="https://pics.dmm.co.jp/digital/video/118kbi00002/118kbi00002jp-4.jpg">
<div class="photo-frame">
<img src="/pics/sample/6qah_4.jpg" title="KBI-002 KANBi専属出演第1弾!旦那を忘れる程 汗だく汁だくで絡み合う 濃厚接吻性交3本番 舌を絡ませ抱き合いながら絶頂に達する密着性交! 織笠るみ - 樣品圖像 - 4">
</div>
</a>
<a class="sample-box" href="https://pics.dmm.co.jp/digital/video/118kbi00002/118kbi00002jp-5.jpg">
<div class="photo-frame">
<img src="/pics/sample/6qah_5.jpg" title="KBI-002 KANBi専属出演第1弾!旦那を忘れる程 汗だく汁だくで絡み合う 濃厚接吻性交3本番 舌を絡ませ抱き合いながら絶頂に達する密着性交! 織笠るみ - 樣品圖像 - 5">
</div>
</a>
<a class="sample-box" href="https://pics.dmm.co.jp/digital/video/118kbi00002/118kbi00002jp-6.jpg">
<div class="photo-frame">
<img src="/pics/sample/6qah_6.jpg" title="KBI-002 KANBi専属出演第1弾!旦那を忘れる程 汗だく汁だくで絡み合う 濃厚接吻性交3本番 舌を絡ませ抱き合いながら絶頂に達する密着性交! 織笠るみ - 樣品圖像 - 6">
</div>
</a>
<a class="sample-box" href="https://pics.dmm.co.jp/digital/video/118kbi00002/118kbi00002jp-7.jpg">
<div class="photo-frame">
<img src="/pics/sample/6qah_7.jpg" title="KBI-002 KANBi専属出演第1弾!旦那を忘れる程 汗だく汁だくで絡み合う 濃厚接吻性交3本番 舌を絡ませ抱き合いながら絶頂に達する密着性交! 織笠るみ - 樣品圖像 - 7">
</div>
</a>
<a class="sample-box" href="https://pics.dmm.co.jp/digital/video/118kbi00002/118kbi00002jp-8.jpg">
<div class="photo-frame">
<img src="/pics/sample/6qah_8.jpg" title="KBI-002 KANBi専属出演第1弾!旦那を忘れる程 汗だく汁だくで絡み合う 濃厚接吻性交3本番 舌を絡ませ抱き合いながら絶頂に達する密着性交! 織笠るみ - 樣品圖像 - 8">
</div>
</a>
<a class="sample-box" href="https://pics.dmm.co.jp/digital/video/118kbi00002/118kbi00002jp-9.jpg">
<div class="photo-frame">
<img src="/pics/sample/6qah_9.jpg" title="KBI-002 KANBi専属出演第1弾!旦那を忘れる程 汗だく汁だくで絡み合う 濃厚接吻性交3本番 舌を絡ませ抱き合いながら絶頂に達する密着性交! 織笠るみ - 樣品圖像 - 9">
</div>
</a>
<a class="sample-box" href="https://pics.dmm.co.jp/digital/video/118kbi00002/118kbi00002jp-10.jpg">
<div class="photo-frame">
<img src="/pics/sample/6qah_10.jpg" title="KBI-002 KANBi専属出演第1弾!旦那を忘れる程 汗だく汁だくで絡み合う 濃厚接吻性交3本番 舌を絡ませ抱き合いながら絶頂に達する密着性交! 織笠るみ - 樣品圖像 - 10">
</div>
</a>
<a class="sample-box" href="https://pics.dmm.co.jp/digital/video/118kbi00002/118kbi00002jp-11.jpg">
<div class="photo-frame">
<img src="/pics/sample/6qah_11.jpg" title="KBI-002 KANBi専属出演第1弾!旦那を忘れる程 汗だく汁だくで絡み合う 濃厚接吻性交3本番 舌を絡ませ抱き合いながら絶頂に達する密着性交! 織笠るみ - 樣品圖像 - 11">
</div>
</a>
<a class="sample-box" href="https://pics.dmm.co.jp/digital/video/118kbi00002/118kbi00002jp-12.jpg">
<div class="photo-frame">
<img src="/pics/sample/6qah_12.jpg" title="KBI-002 KANBi専属出演第1弾!旦那を忘れる程 汗だく汁だくで絡み合う 濃厚接吻性交3本番 舌を絡ませ抱き合いながら絶頂に達する密着性交! 織笠るみ - 樣品圖像 - 12">
</div>
</a>
<a class="sample-box" href="https://pics.dmm.co.jp/digital/video/118kbi00002/118kbi00002jp-13.jpg">
<div class="photo-frame">
<img src="/pics/sample/6qah_13.jpg" title="KBI-002 KANBi専属出演第1弾!旦那を忘れる程 汗だく汁だくで絡み合う 濃厚接吻性交3本番 舌を絡ませ抱き合いながら絶頂に達する密着性交! 織笠るみ - 樣品圖像 - 13">
</div>
</a>
</div>这你妈的这种格式简直就是爬虫初学者的天堂了,都不需要使用Selenium/Playwright这两个东西了,直接一个requests + BeautifulSoup简单解析就行了。
我不去爬你,这简直是天理难容了。
所以,具体的Python代码如下所示
- 配置文件
- series_image_cover_config.py
python
series = "KBI"
start_code = 1
end_code = 100- 实际解析以及下载、程序主入口的内容
- series_image_cover_download.py
python
import os
import asyncio
import aiohttp
from bs4 import BeautifulSoup
import urllib3
from series_image_cover_config import *
# 禁用 InsecureRequestWarning 警告
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
host = "https://www.javbus.com"
HEADERS = {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Language": "zh-CN,zh;q=0.9",
}
async def download_image_cover(session: aiohttp.ClientSession, name: str, series: str):
URL = f"{host}/{name}"
SAVE_DIR = f"./{series}"
os.makedirs(SAVE_DIR, exist_ok=True)
try:
async with session.get(URL, timeout=aiohttp.ClientTimeout(total=10), ssl=False) as response:
if response.status != 200:
print(f"[{name}] 页面不存在,跳过")
return
html = await response.text()
soup = BeautifulSoup(html, "html.parser")
title = soup.find("title")
cover_tag = soup.select_one("a.bigImage img")
if not cover_tag:
print(f"[{name}] 无封面,跳过")
return
cover_url = cover_tag["src"]
if cover_url.startswith("/"):
cover_url = host + cover_url
ext = os.path.splitext(cover_url)[1]
cover_path = os.path.join(SAVE_DIR, f"{name}{ext}")
async with session.get(
cover_url,
headers={"Referer": URL, **HEADERS},
ssl=False
) as img_response:
if img_response.status == 200:
with open(cover_path, "wb") as f:
f.write(await img_response.read())
print(f"[{name}] 下载成功 → {cover_path}")
print(f"[{name}] 标题:{title.text.strip() if title else '无标题'}")
else:
print(f"[{name}] 图片下载失败,状态码:{img_response.status}")
except asyncio.TimeoutError:
print(f"[{name}] 请求超时,跳过")
except Exception as e:
print(f"[{name}] 发生错误:{e}")
return
async def start_to_download():
names = [
f"{SERIES}-{code:03d}" for code in range(max(1, START_CODE), END_CODE)
]
connector = aiohttp.TCPConnector(limit=10)
async with aiohttp.ClientSession(
connector=connector,
headers=HEADERS
) as session:
tasks = [download_image_cover(session, name, SERIES) for name in names]
batch_size = 10
for i in range(0, len(tasks), batch_size):
batch = tasks[i:i+batch_size]
await asyncio.gather(*batch)
await asyncio.sleep(0.5) # 防止请求太快,被他们知道这是来自脚本的下载解析行为
if __name__ == "__main__":
asyncio.run(start_to_download())程序运行方式
shell
python ./series_image_cover_download.py上面的爬取方式,是针对一个大系列的番号进行爬取的。
番号这个概念,就是某某厂商的一个系列,有点像买手机,比如三星手机,它会有外折叠机、内折叠机、直屏机等等,一一对应到Galaxy Z-Fold系列、Galaxy Z-Flip系列和Galaxy S系列。
那么相应地在这里,黄色电影本身就是一个三星手机概念,而番号则是如同手机系列的概念,比如KBI什么的,而具体的片号,则是有番号 + 数字组成,比如 KBI-002 这种格式。
那么针对想下载某一个片的所有详情图,就是解析样品图片这个区域的内容,那么具体的代码如下所示:
special_image_detail_config.py
python
videos = [
{
"name" : "KBI-001",
},
{
"name": "KBI-002",
}
]- special_image_detail_download.py
python
import os
import requests
from bs4 import BeautifulSoup
host = "https://www.javbus.com"
def image_download(names, onlyFolderImage = False):
for name in names:
URL = host + "/" + name
SAVE_DIR = "./{}".format(name)
HEADERS = {
"authority": "www.javbus.com",
"method": "GET",
"path": f"/{name}",
"scheme": "https",
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
"accept-encoding": "gzip, deflate, br, zstd",
"accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
"cookie": "PHPSESSID=nnelcojatbmsjh2s02i4m0fno4; existmag=mag; _tea_utm_cache_10000007=undefined",
"dnt": "1",
"priority": "u=0, i",
"sec-ch-ua": '"Chromium";v="140", "Not=A?Brand";v="24", "Microsoft Edge";v="140"',
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": '"macOS"',
"sec-fetch-dest": "document",
"sec-fetch-mode": "navigate",
"sec-fetch-site": "none",
"sec-fetch-user": "?1",
"upgrade-insecure-requests": "1",
"user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/140.0.0.0 Safari/537.36 Edg/140.0.0.0"
}
os.makedirs(SAVE_DIR, exist_ok=True)
session = requests.Session()
response = session.get(URL, headers=HEADERS)
soup = BeautifulSoup(response.text, "html.parser")
title_tag = soup.find("title")
title = title_tag.text.strip() if title_tag else "unknown_title"
print("页面标题:", title)
cover_tag = soup.select_one("a.bigImage img")
if cover_tag:
cover_url = cover_tag["src"]
if cover_url.startswith("/"):
cover_url = host + cover_url
ext = os.path.splitext(cover_url)[1]
cover_path = os.path.join(SAVE_DIR, f"cover{ext}")
cover_path = cover_path.replace("cover.jpg", "folder.jpg")
cover_headers = HEADERS.copy()
cover_headers["referer"] = URL
r = session.get(cover_url, headers=cover_headers, stream=True)
with open(cover_path, "wb") as f:
for chunk in r.iter_content(1024):
f.write(chunk)
print("封面图已下载:", cover_path)
if not onlyFolderImage:
sample_tags = soup.select("a.sample-box")
if not sample_tags:
print("没有样本图")
else:
for idx, a_tag in enumerate(sample_tags, 1):
img_url = a_tag.get("href")
if not img_url:
continue
ext = os.path.splitext(img_url)[1]
img_path = os.path.join(SAVE_DIR, f"sample_{idx}{ext}")
r = session.get(img_url, headers=HEADERS, stream=True)
with open(img_path, "wb") as f:
for chunk in r.iter_content(1024):
f.write(chunk)
print(f"样本图 {idx} 已下载:", img_path)
if __name__ == "__main__":
from special_image_detail_config import videos
image_download([video["name"] for video in videos])程序运行方式
shell
python ./special_image_detail_download.py- 对于电影的下载部分
- 一般来说,类似像JavBus这些地方,都会提供一些磁力链接,一般来说直接可以使用Motrix去进行下载,但是Motrix也是存在很大的问题,特别是下载这种不太合法的影片的时候,他的下载速度堪忧。
- 两种解决方案:
- 其一是采用:
- 夸克网盘或者迅雷的会员版 去下载,虽然他们的云盘也是会屏蔽这一类的影片资源,但是你下载你的本地,这是没有任何问题的。
- 第二种方式是:
- 很多人会去Miss AV这种聚合平台观看这一类的影片,Miss AV的影片一般是采用M3U8的格式输出的,于是 ->
- 我提供了另一个工具,可以下载这一类的M3U8的视频,详细可以观看这一篇关于M3U下载的文章
- 其一是采用:
重要声明
- 如果除我自己之外的人看到这篇文章,自己拿这段代码在内网玩一下就好了,不要去做什么造成更大影响面的法律风险事宜。
- 这真的是会被抓的!