尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

Python全站名言数据采集实战与反爬策略

Python全站名言数据采集实战与反爬策略 1. 项目概述Python全站名言数据采集实战这个项目源于我最近为一家文化类创业公司做的数据采集需求。他们需要建立一个包含古今中外名人名言的数据仓库用于内容推荐系统的训练。市面上虽然有现成的名言数据集但要么分类体系不符合需求要么数据质量参差不齐。于是我们决定自己动手用Python构建一个高效、稳定的全站名言采集系统。全站采集与普通单页采集最大的区别在于需要处理复杂的网站结构和反爬机制。以名言网站为例典型的结构包含按作者索引页、按分类索引页、名言详情页有些还有用户评论和评分系统。我们的爬虫需要像一位耐心的图书管理员系统地遍历每个书架索引页记录每本书的位置详情页URL最后摘抄其中的精华内容名言文本。提示在开始前请确保已了解目标网站的robots.txt协议商业用途的数据采集务必获得官方授权。本文以技术学习为目的采集频率控制在合理范围。2. 技术选型与工具准备2.1 核心工具链选择经过对比测试我们最终确定的工具组合是请求库Requests Retrying而非Scrapy框架适合中等规模的定向采集解析库PyQuery lxml比BeautifulSoup更快的XML解析性能并发控制ThreadPoolExecutor配合Semaphore精确控制并发数存储方案MySQL关系型存储适合结构化名言数据 本地JSON备份# 基础依赖安装 pip install requests retrying pyquery lxml mysql-connector-python2.2 反爬应对策略名言类网站常见的反爬手段和应对方案反爬类型特征解决方案UA检测返回403状态码轮换User-Agent池IP限制突然大量503错误1. 使用代理IP 2. 降低请求频率行为验证弹出验证码1. 控制点击间隔 2. 使用selenium备用方案参数加密动态生成token分析前端JS生成逻辑我们在项目中创建了智能请求间隔控制器会根据响应时间动态调整采集速度class SmartDelay: def __init__(self, base_delay1.0): self.last_response_time None self.base_delay base_delay def wait(self, response_time): if self.last_response_time: delta response_time - self.last_response_time self.base_delay delta * 0.5 # 响应变慢就增加延迟 self.last_response_time response_time time.sleep(max(0.5, self.base_delay)) # 不低于0.5秒3. 核心采集逻辑实现3.1 网站结构分析以某知名名言网站为例其数据层级为分类页文学/哲学/科学等→ 2. 子分类页按国家/时代→ 3. 作者列表页 → 4. 作者详情页 → 5. 单条名言页我们采用广度优先(BFS)的采集策略def crawl_site(start_url): visited set() queue deque([start_url]) while queue: current_url queue.popleft() if current_url in visited: continue html download_page(current_url) page_type classify_page(html) if page_type category: new_urls extract_category_links(html) queue.extend(new_urls) elif page_type author: new_urls extract_author_links(html) queue.extend(new_urls) elif page_type quote: save_quote(extract_quote_data(html)) visited.add(current_url)3.2 数据提取技巧名言页面通常包含以下字段名言正文作者信息分类标签发表日期如有点赞/收藏数如有使用PyQuery提取的示例def extract_quote_data(html): doc pq(html) return { content: doc(.quote-text).text().strip(), author: { name: doc(.author-name).text(), birth: doc(.author-birth).attr(data-year), }, tags: [pq(tag).text() for tag in doc(.quote-tags a)], meta: { likes: int(doc(.like-count).text()), date: parse_date(doc(.publish-date).text()) } }注意实际项目中应该添加字段有效性验证比如检查content是否为空date格式是否合法等4. 数据存储与清洗4.1 数据库设计我们使用MySQL存储结构化数据主要表结构如下CREATE TABLE authors ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(100) NOT NULL, birth_year SMALLINT, death_year SMALLINT, country VARCHAR(50), INDEX (name) ); CREATE TABLE quotes ( id INT AUTO_INCREMENT PRIMARY KEY, content TEXT NOT NULL, author_id INT NOT NULL, origin_url VARCHAR(255), publish_date DATE, FOREIGN KEY (author_id) REFERENCES authors(id), FULLTEXT INDEX (content) ); CREATE TABLE tags ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50) NOT NULL UNIQUE ); CREATE TABLE quote_tags ( quote_id INT NOT NULL, tag_id INT NOT NULL, PRIMARY KEY (quote_id, tag_id), FOREIGN KEY (quote_id) REFERENCES quotes(id), FOREIGN KEY (tag_id) REFERENCES tags(id) );4.2 数据清洗策略原始采集数据常见问题及处理方法编码问题使用ftfy库修复mojibake字符import ftfy fixed_text ftfy.fix_text(raw_text)重复名言检测计算文本指纹进行去重def generate_fingerprint(text): text re.sub(r\s, , text.lower()) return hashlib.md5(text.encode()).hexdigest()作者信息补全对于只有名字没有详细信息的作者调用维基百科API补全5. 高级技巧与优化方案5.1 增量采集实现为避免重复采集整个网站我们设计了基于时间戳的增量采集方案在数据库中记录每个页面的最后更新时间采集前先检查Last-ModifiedHTTP头对于动态页面通过比较页面关键区域的MD5值判断是否更新def need_update(url, last_crawl_time): headers {If-Modified-Since: last_crawl_time} resp requests.head(url, headersheaders) if resp.status_code 304: return False # 对动态内容计算关键区域hash current_hash calc_content_hash(url) return current_hash ! get_stored_hash(url)5.2 断点续采机制对于大规模采集实现断点恢复功能至关重要定期将待采集URL队列保存到磁盘使用atexit注册退出时的保存钩子程序重启时从检查点恢复import atexit import pickle class CrawlState: def __init__(self): self.queue deque() self.visited set() atexit.register(self.save_state) def save_state(self): with open(crawl_state.pkl, wb) as f: pickle.dump({queue: list(self.queue), visited: list(self.visited)}, f) def load_state(self): try: with open(crawl_state.pkl, rb) as f: data pickle.load(f) self.queue deque(data[queue]) self.visited set(data[visited]) except FileNotFoundError: pass6. 常见问题与解决方案6.1 反爬突破实战记录案例某网站使用动态token验证解决步骤使用Chrome开发者工具分析XHR请求发现每个请求需要携带X-Token头逆向工程找到token生成JS代码用PyExecJS执行关键JS函数import execjs with open(token_generator.js) as f: js_code f.read() ctx execjs.compile(js_code) token ctx.call(generateToken, /api/quotes) headers {X-Token: token}6.2 性能优化指标优化前后的性能对比采集10万条名言指标优化前优化后总耗时6小时2.5小时内存占用1.2GB350MB失败率15%3%数据完整度92%99.7%关键优化点使用连接池管理数据库连接将图片等非关键资源采集改为延迟加载实现智能重试机制对503错误自动退避7. 项目扩展方向7.1 数据质量监控建立自动化质量检查流程内容重复检测作者信息完整性检查非ASCII字符统计情感极性分析检测异常负面内容def quality_check(quote): checks { length_check: len(quote[content]) 10, author_check: bool(quote[author]), duplicate_check: not is_duplicate(quote[content]), sentiment_check: analyze_sentiment(quote[content])[polarity] -0.5 } return checks7.2 自动化更新管道使用Airflow构建数据管道from airflow import DAG from airflow.operators.python import PythonOperator dag DAG(quote_pipeline, schedule_intervalweekly) def crawl_task(): # 采集逻辑 def clean_task(): # 清洗逻辑 def analyze_task(): # 分析逻辑 crawl PythonOperator(task_idcrawl, python_callablecrawl_task, dagdag) clean PythonOperator(task_idclean, python_callableclean_task, dagdag) analyze PythonOperator(task_idanalyze, python_callableanalyze_task, dagdag) crawl clean analyze在实际部署中发现使用Docker容器化爬虫可以更好地管理依赖和环境。我们为项目创建了包含所有依赖的Docker镜像并通过环境变量控制运行参数FROM python:3.9 WORKDIR /app COPY requirements.txt . RUN pip install -r requirements.txt COPY . . CMD [python, main.py, --mode, ${RUN_MODE}]
返回列表