ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

用Python爬取Top100电视剧并存入MySQL:TaoToken统一Key通道下的采集与入库实战

用Python爬取Top100电视剧并存入MySQL:TaoToken统一Key通道下的采集与入库实战 1. 从榜单页到数据库Python 爬取 Top100 电视剧并写入 MySQL 的完整链路很多人第一次做爬虫练手都会选「榜单类」页面结构规整、字段清晰、数据量不大正好适合把「请求 → 解析 → 清洗 → 入库」这条链路走一遍。这次要做的就是把 Top100 电视剧榜单抓下来存进 MySQL方便后面做排序、筛选、导出 Excel 之类的操作。核心检索词就三个python、爬取、mysql 数据库整篇文章围绕这三件事展开。适合谁看如果你已经会写for循环、装过 Python 包但还没完整跑通过一次「采集 落库」这篇就是给你准备的。我会把每一步拆开先建表再写采集脚本然后处理空值、分页、编码这些坑最后用 SQL 核对数据。全程用requestsBeautifulSouppymysql不引入重框架复制就能跑。另外说一句采集脚本里如果涉及调用大模型做字段清洗、简介摘要、导演名归一化我会用 TaoToken 的统一 Key 通道来演示这样你不用在多个平台之间来回切 Key。下面正式开始。2. 环境准备与 TaoToken 统一 Key 通道配置python 爬虫 mysql 入库前置2.1 先装依赖别急着写代码打开终端建一个干净的虚拟环境避免和系统里的包打架python -m venv venv # Windows venv\Scripts\activate # macOS / Linux source venv/bin/activate pip install requests beautifulsoup4 pymysql lxmllxml是解析器比默认的html.parser快遇到不规范 HTML 也更稳。装完可以用pip list确认一下版本。2.2 MySQL 侧的准备本地装好 MySQL 后先建库CREATE DATABASE tv_top100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;注意用utf8mb4不要用老的utf8。电视剧名里偶尔会有生僻字或特殊符号utf8mb4能少踩很多编码坑。建完库记下 host、port、user、password后面脚本里要用。2.3 TaoToken 统一 Key 通道是什么为什么这里要用采集本身不需要大模型但实际项目里榜单数据往往要做二次加工比如把「导演xxx」这种带前缀的字段拆干净、把简介压缩成一句话、把主演列表标准化。这些用规则写会越写越乱交给模型做反而省事。TaoToken 提供的是一个统一 Key 通道你拿一个 Key就能在同一个入口调用不同模型不用为每个模型单独申请账号、单独配 Base URL。对爬虫项目来说好处是清洗逻辑可以集中在一处脚本里只维护一个 Key。配置入口在这里控制台拿 Keyhttps://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentapi_keys接入文档看 Base URL 和参数格式https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentdocAPI 基础地址是https://taotoken.net/api注意这个地址后面不加任何查询参数。Key 拿到后不要硬编码进脚本用环境变量存# macOS / Linux export TAOTOKEN_API_KEY你的Key # Windows PowerShell $env:TAOTOKEN_API_KEY你的Key这样脚本可以提交到 Git 而不泄露凭证。下面进入建表和采集部分。3. 建表 SQL 与可复制采集脚本python 爬取电视剧写入 mysql 数据库3.1 建表字段类型要留余量先建表。字段设计上movie_name用varchar(100)导演和主演可能有多人给到varchar(200)简介用varchar(500)更保险因为有些简介比想象中长USE tv_top100; CREATE TABLE movies7 ( m_id INT PRIMARY KEY AUTO_INCREMENT, movie_name VARCHAR(100) NOT NULL, movie_daoyan VARCHAR(200) NULL, movie_zhuyan VARCHAR(200) NOT NULL, movie_jianjie VARCHAR(500) NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;我额外加了created_at方便后面判断数据是哪次采集的。movie_jianjie允许为 NULL因为榜单里确实存在没有简介的条目这点很关键后面脚本里要专门判断。3.2 采集脚本分页 空值判断 批量入库下面这份脚本可以直接复制改掉数据库密码和库名即可。我把分页、空值判断、异常捕获都写进去了import os import time import requests import pymysql from bs4 import BeautifulSoup DB_CONFIG { host: 127.0.0.1, port: 3306, user: root, passwd: 你的数据库密码, db: tv_top100, charset: utf8mb4, } HEADERS { User-Agent: ( Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 ), Referer: http://www.mtime.com/top/tv/top100/, Accept-Language: zh-CN,zh;q0.9, } BASE_URL http://www.mtime.com/top/tv/top100/index-{}.html def fetch_page(page: int) - str: url BASE_URL.format(page) resp requests.get(url, headersHEADERS, timeout10) resp.raise_for_status() resp.encoding utf-8 return resp.text def parse_page(html: str): soup BeautifulSoup(html, lxml) container soup.find(div, class_top_list) if container is None: return [] rows [] for film in container.find_all(li): h2 film.find(h2) if h2 is None: continue name h2.get_text(stripTrue) ps film.find_all(p) daoyan ps[0].get_text(stripTrue) if len(ps) 0 else zhuyan ps[1].get_text(stripTrue) if len(ps) 1 else jianjie_tag film.find(p, class_mt3) jianjie jianjie_tag.get_text(stripTrue) if jianjie_tag is not None else rows.append((name, daoyan, zhuyan, jianjie)) return rows def save_rows(conn, rows): sql ( INSERT INTO movies7 (movie_name, movie_daoyan, movie_zhuyan, movie_jianjie) VALUES (%s, %s, %s, %s) ) with conn.cursor() as cursor: cursor.executemany(sql, rows) conn.commit() def main(): conn pymysql.connect(**DB_CONFIG) total 0 try: for page in range(1, 6): html fetch_page(page) rows parse_page(html) if rows: save_rows(conn, rows) total len(rows) print(fpage {page} - {len(rows)} rows) time.sleep(1.5) finally: conn.close() print(fdone, total {total} rows)几个细节说明一下。第一resp.encoding utf-8要显式设置否则requests有时会猜错编码导致中文变乱码。第二executemany比循环单条execute快很多100 条数据一次提交减少网络往返。第三time.sleep(1.5)是礼貌间隔别把对方服务器打崩也降低被限流的概率。3.3 用 TaoToken 做字段清洗可选但推荐如果榜单里导演字段带「导演」前缀或者简介里有换行、多余空格可以用模型统一清洗。下面这段是调用示例Base URL 用https://taotoken.net/apiimport os import requests def clean_field(raw_text: str) - str: api_key os.environ[TAOTOKEN_API_KEY] payload { model: gpt-4o-mini, messages: [ {role: system, content: 你是数据清洗助手只输出清洗后的纯文本不要解释。}, {role: user, content: f去掉前缀和多余空白保留人名或简介{raw_text}}, ], temperature: 0, } resp requests.post( https://taotoken.net/api/v1/chat/completions, headers{ Authorization: fBearer {api_key}, Content-Type: application/json, }, jsonpayload, timeout30, ) resp.raise_for_status() return resp.json()[choices][0][message][content].strip()注意choices这个字段如果返回结构不对多半是请求体格式或模型名写错了后面排障章节会讲。清洗逻辑建议放在入库前先清洗再executemany避免脏数据进库。4. 运行验证与数据核对python 爬虫 mysql 落库结果检查4.1 跑脚本看输出保存为spider.py运行python spider.py正常输出类似page 1 - 20 rows page 2 - 20 rows page 3 - 20 rows page 4 - 20 rows page 5 - 20 rows done, total 100 rows如果某一页返回 0 行先别慌可能是那一页结构不同或者被限流返回了验证页。把fetch_page返回的 HTML 存下来看一眼就知道。4.2 用 SQL 核对数据进 MySQL 客户端跑几条核对语句-- 总数 SELECT COUNT(*) FROM movies7; -- 看前 10 条 SELECT m_id, movie_name, movie_daoyan, movie_zhuyan FROM movies7 LIMIT 10; -- 找空简介 SELECT COUNT(*) FROM movies7 WHERE movie_jianjie IS NULL OR movie_jianjie ; -- 找重复剧名 SELECT movie_name, COUNT(*) AS c FROM movies7 GROUP BY movie_name HAVING c 1;如果总数是 100说明分页抓全了。如果空简介数量和你预期不符回去检查jianjie_tag的判断逻辑。重复剧名可能是榜单本身就有也可能是脚本跑了两次加个唯一索引或者先清表再跑就能解决。4.3 验证 TaoToken 通道是否通单独测一下清洗接口确认 Key 和 Base URL 都对curl https://taotoken.net/api/v1/chat/completions \ -H Authorization: Bearer $TAOTOKEN_API_KEY \ -H Content-Type: application/json \ -d {model:gpt-4o-mini,messages:[{role:user,content:回复ok}]}返回里能看到choices数组和内容就说明通道正常。想直接在网页里试模型效果可以用模型对话入口https://taotoken.net/model-chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentmodel_chat5. 常见报错排查401、local proxy failed、reading choices、OAuth5.1 401 Unauthorized最常见的原因是 Key 没读到。检查环境变量echo $TAOTOKEN_API_KEY如果是空的说明当前终端没加载。Windows 下用$env:TAOTOKEN_API_KEY查看。还有一种情况是 Key 复制时带了空格或换行strip()一下再拼进 Header。5.2 local proxy failed这个报错通常出现在请求发不出去的时候本质是网络层没通。先确认目标地址能访问curl -I https://taotoken.net/api如果这里就失败说明本机网络或 DNS 有问题检查一下网络设置别在代码里硬塞代理配置。代码里requests默认会读系统环境变量里的代理设置如果之前设过又忘了清也会报这个错。用proxies{http: None, https: None}显式关掉即可。5.3 reading choices / KeyError: choices这个报错说明返回的 JSON 里没有choices字段。原因一般有三类一是请求体格式不对比如messages写成了字符串二是模型名写错服务端返回了错误对象三是 Key 无效返回的是鉴权错误。排查方法很简单把原始返回打出来print(resp.status_code) print(resp.text)看到具体错误信息基本就能定位。别直接resp.json()[choices]先判断状态码。5.4 OAuth 相关报错如果你用的是某些命令行工具比如 Claude Code 这类可能会遇到 OAuth 登录失败。这类工具通常需要配置三件套Base URL、Key、Model ID。以 Claude Code 为例配置项写在settings.json里{ env: { ANTHROPIC_BASE_URL: https://taotoken.net/api, ANTHROPIC_API_KEY: 你的Key, ANTHROPIC_MODEL: claude-3-5-sonnet-20241022 } }三件套缺一不可。Base URL 写错、Key 过期、Model ID 拼错都会报 OAuth 或鉴权失败。如果你用的是 Codex 类工具配置写在auth.json里字段名不同但逻辑一样。Cline 的 MCP 配置也是同理Base URL Key Model ID 三件套对齐就行。5.5 入库报错Incorrect string value这个多半是字符集问题。确认三处建库时用了utf8mb4、建表时用了utf8mb4、连接时charsetutf8mb4。三处一致基本就不会报。如果还有问题检查是不是有 emoji 或特殊符号utf8mb4是能存的老的utf8不行。6. 把采集链路固定下来长期跑批与 Coding Plan脚本跑通一次不难难的是长期稳定跑。我的做法是把采集逻辑封装成函数加一个定时任务每天跑一次入库前先按movie_name去重。这样榜单更新时新数据自动进来老数据不重复。如果你后面想把这条链路扩展成「采集 模型清洗 自动摘要 定时入库」的完整流水线调用量会上来这时候用 Coding Plan 会更划算额度集中管理不用每次单独算。入口在这里https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentcoding_plan最后留一个实用技巧采集脚本里所有可能变化的配置URL、表名、字段映射都抽成常量放文件顶部下次换榜单只改几行不用翻整个脚本。数据库密码和 Key 一律走环境变量别写死在代码里。这两条做到了你的爬虫脚本就能从「练手」变成「能长期用」的工具。
返回列表