ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Python爬虫入门:从零构建数据采集器,掌握requests与BeautifulSoup实战

Python爬虫入门:从零构建数据采集器,掌握requests与BeautifulSoup实战 简介这是一套面向编程零基础学习者的Python网络爬虫实战入门资源聚焦HTML解析驱动的数据采集与多格式存储全流程解决初学者从环境搭建、网页请求、结构化提取到持久化落地的核心痛点。资源包共30个文件含17个可直接运行的Python脚本覆盖requests抓取、BeautifulSoup/lxml解析、维基百科词条递归采集、链接去重、数据库写入等典型场景、6份PDF技术文档含《Web Scraping with Python》《Beautiful Soup官方文档》《Scrapy入门教程》等权威参考资料、5个XML配置与项目元数据文件以及README说明和IDE配置文件整体96.04MB结构清晰、即学即用。目前已有39人下载学习。学习者可获得完整可复现的爬虫项目链从静态页面解析、正则提取图片、新闻数据采集到六度空间链接遍历、维基百科结构化存储配套代码均经过实践验证并融入反爬应对思路与合规采集提醒为后续进阶数据科学与信息检索打下扎实工程基础。1. 项目概述从零开始构建你的第一个数据采集器看到这个标题很多刚接触编程的朋友可能会觉得“爬虫”是个高深莫测的技术需要复杂的算法和深厚的计算机功底。其实不然用Python写一个基础的、基于HTML解析的爬虫就像学骑自行车一样一旦掌握了平衡剩下的就是踩踏板了。这个项目就是带你从零开始一步步搭建一个能自动从网页上抓取你需要的数据并把它规规矩矩存起来的工具。无论你是想收集某个商品的价格变化、追踪新闻热点还是想为自己的数据分析项目找点“原料”这套方法都为你提供了一个清晰、可靠的起点。核心思路很简单模拟浏览器访问网页 - 获取网页的HTML源代码 - 从源代码中“解析”出我们需要的数据 - 把数据保存到本地文件或数据库。整个过程我们主要会用到两个Python库requests负责网络请求BeautifulSoup负责解析HTML。数据存储方面我们会从最简单的CSV文件开始再延伸到轻量级的SQLite数据库。别担心我会把每一步为什么这么做、可能会遇到什么坑、以及怎么绕过去都掰开揉碎了讲清楚。即使你昨天才安装好Python今天也能跟着做出一个能跑起来的爬虫。2. 环境准备与核心工具解析2.1 Python环境与必备库安装工欲善其事必先利其器。首先确保你的电脑上已经安装了Python。去Python官网下载最新稳定版比如3.8以上版本安装即可记得在安装时勾选“Add Python to PATH”这样在命令行里就能直接使用python和pip命令了。安装好后打开你的命令行Windows上是CMD或PowerShellMac/Linux上是终端我们来安装这个项目最核心的三个库。pip install requests beautifulsoup4 lxmlrequests这是Python里最受欢迎的HTTP库没有之一。它的口号是“HTTP for Humans”意思是对人类极其友好。我们用它将一个网址URL发送给服务器然后接收服务器返回的网页内容。相比于Python自带的urllibrequests的API简洁直观得多。BeautifulSoup我们的“解析神器”。网页的HTML代码就像一棵倒着长的树DOM树有根节点、分支和叶子。BeautifulSoup能帮我们在这棵树上轻松地导航、搜索和修改从而精准地找到藏在标签里的数据。它支持多种解析器我们安装的lxml就是其中速度最快、容错性较好的一个。lxml这是一个高性能的HTML/XML解析器。BeautifulSoup本身只是一个“包装器”它需要依赖像lxml或html.parser这样的解析器来实际处理HTML字符串。选择lxml是因为它在处理复杂或破损的HTML时表现更稳定速度也更快。注意有些教程可能会提到urllib或urllib3。对于新手我强烈建议直接从requests开始。它封装了连接池、重试、编码处理等底层细节让你能更专注于业务逻辑而不是处理各种网络异常。这是从“能用”到“好用”的关键一步。2.2 开发工具与调试技巧写代码需要一个顺手的编辑器。对于新手我推荐使用Visual Studio Code (VSCode)。它免费、轻量、插件生态丰富。安装后记得安装Python扩展它能提供代码高亮、智能提示、调试等功能极大提升开发效率。在开始爬虫前还有一个至关重要的准备工作使用浏览器开发者工具。以Chrome浏览器为例在任何网页上右键点击选择“检查”Inspect就能打开开发者工具。元素检查Elements这个标签页展示了网页渲染后的完整DOM树。你可以把鼠标移到代码上页面对应的元素会高亮显示。这是我们寻找数据所在HTML标签结构的主要战场。网络请求Network这个标签页记录了浏览器发出的所有请求HTML、图片、JavaScript、API接口等。刷新页面后这里会列出所有请求。找到最主要的文档请求通常是第一个类型为document点击查看它的Headers和Response。Headers里可以看到我们模拟请求时需要的关键信息如User-AgentResponse里可以看到服务器返回的原始HTML有时和“元素”标签里看到的不一样因为后者是经过JavaScript修改后的爬虫获取的是Response里的内容。控制台Console可以执行JavaScript代码有时用于测试某些动态加载数据的接口。一个实用的调试技巧在写解析代码时可以先将网页的HTML保存到一个本地文件然后用BeautifulSoup加载这个文件进行解析测试。这样可以避免频繁网络请求快速调整你的解析规则等规则稳定了再接入真实的网络请求。具体操作是在开发者工具的“元素”标签页右键点击html标签选择“Copy - Copy outer HTML”然后粘贴保存为test.html文件。3. 核心步骤一发起请求与获取HTML3.1 构建一个稳健的HTTP请求拿到了网址我们第一步就是让Python程序去“访问”它。直接用requests.get()是最简单的但一个健壮的爬虫不能这么“裸奔”。import requests url https://example.com # 替换成你的目标网址 # 1. 设置请求头这是模仿浏览器的关键一步 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 } try: # 2. 发送GET请求并带上请求头 response requests.get(url, headersheaders, timeout10) # 3. 检查请求是否成功状态码为200 response.raise_for_status() # 4. 自动推断编码并获取网页文本内容 response.encoding response.apparent_encoding html_content response.text print(成功获取网页内容长度, len(html_content)) except requests.exceptions.RequestException as e: print(f请求出错{e}) except Exception as e: print(f发生未知错误{e})关键点解析User-Agent这是HTTP请求头中最重要的字段之一它告诉服务器你是什么类型的客户端浏览器。很多网站会屏蔽没有User-Agent或使用默认PythonUser-Agent的请求认为它是爬虫。所以我们这里伪装成一个常见的Chrome浏览器。你可以在自己浏览器的开发者工具“Network”标签里找到任意一个请求复制它的User-Agent值来用。timeout设置超时时间单位秒。网络状况不稳定时如果一个请求长时间没响应程序会一直卡住。设置超时后超过时间会自动抛出异常避免程序“假死”。raise_for_status()这是一个非常实用的方法。如果响应状态码不是200成功比如404找不到或503服务不可用它会抛出一个HTTPError异常让我们能及时处理错误而不是拿着一个错误的页面内容去解析。编码处理网页可能有各种编码如UTF-8, GBK。response.apparent_encoding是requests库根据内容分析出的可能编码通常比较准确。直接设置response.encoding后续response.text就会用这个编码来解码字节流避免中文乱码。3.2 处理常见的反爬机制与请求异常直接get可能不会一帆风顺网站可能会有一些简单的防护。处理简单的重定向requests默认会自动处理重定向状态码301/302。你不需要额外代码。如果需要观察重定向路径可以设置allow_redirectsFalse并手动处理response.headers[Location]。处理连接错误和超时我们已经用try...except包裹了请求并设置了timeout。对于可能出现的ConnectionError网络连接问题、Timeout超时requests.exceptions.RequestException是它们的基类可以一并捕获。添加请求间隔如果你需要连续爬取一个网站的多个页面千万不要一口气快速请求。这会给对方服务器造成压力容易被封IP。一个基本的道德和技巧是在请求之间添加随机延时。import time import random def safe_get(url, headers): try: resp requests.get(url, headersheaders, timeout10) resp.raise_for_status() resp.encoding resp.apparent_encoding return resp.text except requests.exceptions.RequestException as e: print(f请求 {url} 失败{e}) return None # 模拟爬取多个页面 url_list [https://example.com/page1, https://example.com/page2] for url in url_list: html safe_get(url, headers) if html: # ... 处理html ... pass # 添加随机延时模拟人类操作。间隔2到5秒。 time.sleep(random.uniform(2, 5))实操心得对于初学者我建议先把一个页面的请求和解析做稳定再考虑多页面爬取。一开始就处理复杂的翻页和并发容易让问题纠缠在一起。另外务必尊重网站的robots.txt协议通常在网站根目录如https://example.com/robots.txt它指明了网站允许和禁止爬虫访问的路径。4. 核心步骤二使用BeautifulSoup解析HTML数据拿到了一长串HTML代码下一步就是从中“挖”出我们想要的数据。这就是BeautifulSoup的舞台。4.1 理解HTML结构与选择器假设我们要爬取一个简单的新闻列表页其HTML结构可能如下div classnews-list article classnews-item h2a href/news/123.htmlPython 3.12 正式发布性能提升显著/a/h2 p classsummaryPython 3.12版本带来了多项性能优化和新特性.../p span classdate2023-10-02/span span classauthor官方团队/span /article article classnews-item h2a href/news/124.htmlBeautifulSoup 4.12 更新日志/a/h2 p classsummary解析库BeautifulSoup发布了新版本.../p span classdate2023-09-28/span span classauthor社区贡献者/span /article /div我们的目标是提取每篇文章的标题、链接、摘要、日期和作者。4.2 多种数据提取方法实战首先我们需要创建一个BeautifulSoup对象来装载和解析HTML。from bs4 import BeautifulSoup # 假设html_content是上一步requests获取的字符串 soup BeautifulSoup(html_content, lxml) # 指定使用lxml解析器方法一通过标签名和属性查找find,find_all这是最常用、最直观的方法。# 1. 找到所有 classnews-item 的 article 标签 news_items soup.find_all(article, class_news-item) # 注意因为class是Python关键字所以BeautifulSoup里用class_来匹配CSS类。 news_list [] for item in news_items: # 2. 在每个item内查找具体元素 # find() 找第一个匹配的find_all() 找所有匹配的 title_tag item.find(h2).find(a) # 先找到h2再找到里面的a标签 title title_tag.text.strip() # .text获取标签内文本.strip()去除首尾空白字符 link title_tag[href] # 获取标签的属性如href summary item.find(p, class_summary).text.strip() date item.find(span, class_date).text.strip() author item.find(span, class_author).text.strip() # 3. 将数据组织成字典方便后续处理 news_data { title: title, link: link, summary: summary, date: date, author: author } news_list.append(news_data) # 打印结果 for news in news_list: print(news)方法二使用CSS选择器select,select_one如果你熟悉CSS这种方法会更强大和简洁。它使用类似jQuery的选择器语法。# 选择所有 classnews-item 的元素 news_items soup.select(article.news-item) for item in news_items: # select_one 相当于 find选择第一个匹配项 title_tag item.select_one(h2 a) # 选择h2标签下的直接子元素a title title_tag.text.strip() link title_tag[href] summary item.select_one(p.summary).text.strip() date item.select_one(span.date).text.strip() author item.select_one(span.author).text.strip() # ... 后续处理同上方法三处理缺失数据与复杂结构现实中的网页不会这么规整可能某些元素缺失。我们需要更健壮的代码。for item in news_items: # 使用try-except或条件判断避免因某个元素找不到而崩溃 title_tag item.find(h2) if title_tag: a_tag title_tag.find(a) title a_tag.text.strip() if a_tag else 无标题 link a_tag[href] if a_tag and a_tag.has_attr(href) else # else: title 无标题 link # # 使用get_text()方法并设置默认值 summary_elem item.find(p, class_summary) summary summary_elem.get_text(stripTrue, default无摘要) # default参数是BeautifulSoup 4.11特性 # 对于更老的版本可以这样写 # summary summary_elem.text.strip() if summary_elem else 无摘要 # 同理处理日期和作者 date_elem item.find(span, class_date) date date_elem.text.strip() if date_elem else 未知日期 author_elem item.find(span, class_author) author author_elem.text.strip() if author_elem else 未知作者 # ... 组装数据注意.text和.get_text()的区别.text返回的是所有子标签文本拼接后的字符串。.get_text()功能更强大可以指定分隔符separator、是否去除空白strip并且在新版本中支持默认值default。在大多数情况下使用.get_text(stripTrue)是更好的选择。实操心得解析HTML时最耗时间的往往不是写代码而是分析网页结构。一定要充分利用浏览器的开发者工具。右键点击你想提取的数据选择“检查”仔细看它外层包裹的标签和类名。优先使用class和id这类具有唯一性的属性进行定位。如果结构非常复杂或动态生成CSS选择器select的层级写法如div.content ul.list li:first-child会比一连串的find更清晰。5. 核心步骤三数据的清洗与存储数据抓取和解析出来后往往是杂乱无章的文本我们需要进行清洗然后选择一种合适的方式存储起来供后续分析使用。5.1 数据清洗与格式化清洗通常在解析出文本后立即进行。常见操作包括去除空白字符使用.strip()、.lstrip()、.rstrip()或get_text(stripTrue)。处理特殊字符和编码有时会遇到HTML实体如nbsp;、amp;。BeautifulSoup在解析时通常会自动转换如果还有残留可以用html.unescape()处理。字符串替换与分割例如日期字符串“发布于2023-10-02”我们只需要“2023-10-02”部分。import html # 示例清洗一条数据 raw_date 发布于2023-10-02 cleaned_date raw_date.strip().replace(发布于, ) print(cleaned_date) # 输出2023-10-02 # 处理可能存在的HTML实体 raw_text Python amp; BeautifulSoup cleaned_text html.unescape(raw_text) print(cleaned_text) # 输出Python BeautifulSoup # 分割字符串获取特定部分 tag_string 科技, 编程, Python tag_list [tag.strip() for tag in tag_string.split(,)] print(tag_list) # 输出[科技, 编程, Python]5.2 存储方案一CSV文件简单通用CSVComma-Separated Values是一种用逗号分隔的纯文本格式可以被Excel、Numbers、Python pandas等几乎所有数据处理工具轻松打开非常适合存储表格数据。import csv # 假设 news_list 是之前解析得到的字典列表 data_to_save news_list # 定义CSV文件的列名表头 fieldnames [title, link, summary, date, author] # 使用‘w’模式写入newline防止在Windows下出现空行 # encodingutf-8-sig 可以让Excel正确识别UTF-8编码的中文 with open(news_data.csv, w, newline, encodingutf-8-sig) as csvfile: writer csv.DictWriter(csvfile, fieldnamesfieldnames) # 写入表头 writer.writeheader() # 写入所有数据行 for news in data_to_save: writer.writerow(news) print(数据已保存到 news_data.csv)关键点解析newline在Python中打开文本文件写入时指定这个参数可以避免在Windows系统上产生额外的空行。encodingutf-8-sigutf-8-sig会在文件开头写入一个BOM字节顺序标记这对于一些老版本的Excel软件识别UTF-8编码的中文至关重要。如果不需要兼容老Excel使用utf-8即可。csv.DictWriter因为我们数据是字典格式用DictWriter非常方便它会自动根据fieldnames将字典的键值对匹配到对应的列。5.3 存储方案二SQLite数据库结构化、可查询当数据量变大或者你需要进行复杂的查询、去重、更新操作时CSV就显得力不从心了。SQLite是一个轻量级的、无需单独服务器进程的数据库整个数据库就是一个文件完美适配个人爬虫项目。import sqlite3 # 1. 连接到数据库如果不存在则会创建 conn sqlite3.connect(news_data.db) cursor conn.cursor() # 2. 创建数据表 create_table_sql CREATE TABLE IF NOT EXISTS news ( id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, link TEXT UNIQUE, -- 设置链接为唯一防止重复爬取 summary TEXT, date TEXT, author TEXT, created_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) cursor.execute(create_table_sql) # 3. 插入数据 insert_sql INSERT OR IGNORE INTO news (title, link, summary, date, author) VALUES (?, ?, ?, ?, ?) # 使用 OR IGNORE当遇到重复的link违反UNIQUE约束时忽略这条插入 for news in news_list: # 将字典中的值按顺序填入 ? 占位符 cursor.execute(insert_sql, ( news[title], news[link], news[summary], news[date], news[author] )) # 4. 提交事务并关闭连接 conn.commit() print(f成功插入 {cursor.rowcount} 条数据到数据库。) cursor.close() conn.close()关键点解析IF NOT EXISTS创建表时使用避免重复创建报错。UNIQUE约束我们将link字段设为唯一。这是爬虫数据存储的一个最佳实践。它可以有效避免因重复运行脚本或爬取到相同链接而导致的数据重复。INSERT OR IGNORE语句会在遇到重复唯一键时静默跳过而不是报错。参数化查询 (?)绝对不要用字符串拼接的方式将变量传入SQL语句如fINSERT ... VALUES ({news[title]})这会导致严重的SQL注入安全风险。使用?作为占位符然后将值以元组形式传给execute()方法让数据库驱动库去安全地处理。AUTOINCREMENT和DEFAULT CURRENT_TIMESTAMPid字段自动增长作为主键。created_time字段会自动记录数据插入的时间对于追踪数据抓取时间很有用。实操心得对于初学者我建议从CSV开始直观简单。当你需要管理成百上千条数据或者想练习数据库操作时再迁移到SQLite。在爬虫项目中去重是关键。除了利用数据库的UNIQUE约束也可以在内存中用Python的set()存储已爬取的链接每次爬取前先判断这对于防止重复请求、提升效率也很有帮助。6. 项目整合与优化构建一个完整的爬虫脚本现在我们把前面所有步骤整合起来形成一个完整的、可复用的爬虫脚本。我们以爬取一个虚构的“书籍信息”网站为例。6.1 完整脚本示例爬取书籍信息import requests from bs4 import BeautifulSoup import sqlite3 import time import random from urllib.parse import urljoin # 用于拼接相对链接为绝对链接 class BookSpider: def __init__(self, base_url, db_pathbooks.db): 初始化爬虫 :param base_url: 网站基础URL :param db_path: SQLite数据库文件路径 self.base_url base_url self.db_path db_path self.headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 } self.init_database() def init_database(self): 初始化数据库和表 conn sqlite3.connect(self.db_path) cursor conn.cursor() cursor.execute( CREATE TABLE IF NOT EXISTS books ( id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, price REAL, author TEXT, rating REAL, link TEXT UNIQUE, created_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ) conn.commit() conn.close() print(数据库初始化完成。) def fetch_page(self, url): 获取页面HTML内容 try: resp requests.get(url, headersself.headers, timeout15) resp.raise_for_status() # 有些网站可能不是utf-8比如gbk这里可以灵活处理 # 如果知道编码可以直接指定resp.encoding gbk resp.encoding resp.apparent_encoding or utf-8 return resp.text except requests.exceptions.RequestException as e: print(f请求失败 {url}: {e}) return None def parse_book_list(self, html): 解析书籍列表页提取每本书的详情页链接 soup BeautifulSoup(html, lxml) book_links [] # 假设每本书的链接在一个 classbook-title 的h3标签下的a标签里 for title_tag in soup.select(h3.book-title): a_tag title_tag.find(a) if a_tag and a_tag.has_attr(href): # 将相对链接转换为绝对链接 full_url urljoin(self.base_url, a_tag[href]) book_links.append(full_url) return book_links def parse_book_detail(self, html, book_url): 解析书籍详情页提取具体信息 soup BeautifulSoup(html, lxml) book_info {} # 提取书名 title_elem soup.find(h1, class_product-title) book_info[title] title_elem.get_text(stripTrue) if title_elem else 未知书名 # 提取价格可能包含货币符号 price_elem soup.find(span, class_price) if price_elem: price_text price_elem.get_text(stripTrue) # 简单清洗价格提取数字部分 import re price_num re.search(r[\d\.], price_text) book_info[price] float(price_num.group()) if price_num else 0.0 else: book_info[price] 0.0 # 提取作者 author_elem soup.find(div, class_author) book_info[author] author_elem.get_text(stripTrue) if author_elem else 未知作者 # 提取评分 rating_elem soup.find(div, class_rating-score) if rating_elem: rating_text rating_elem.get_text(stripTrue) book_info[rating] float(rating_text) if rating_text.replace(., , 1).isdigit() else 0.0 else: book_info[rating] 0.0 book_info[link] book_url return book_info def save_to_db(self, book_info): 保存单条书籍信息到数据库 conn sqlite3.connect(self.db_path) cursor conn.cursor() try: cursor.execute( INSERT OR IGNORE INTO books (title, price, author, rating, link) VALUES (?, ?, ?, ?, ?) , (book_info[title], book_info[price], book_info[author], book_info[rating], book_info[link])) conn.commit() if cursor.rowcount 0: print(f已保存: {book_info[title]}) else: print(f已跳过重复: {book_info[title]}) except sqlite3.Error as e: print(f数据库保存失败 {book_info[title]}: {e}) finally: conn.close() def crawl(self, start_page1, end_page3): 主爬取流程遍历列表页抓取详情页 for page in range(start_page, end_page 1): print(f\n正在爬取第 {page} 页...) list_url f{self.base_url}/list?page{page} # 假设分页URL格式如此 list_html self.fetch_page(list_url) if not list_html: print(f第 {page} 页列表获取失败跳过。) continue book_urls self.parse_book_list(list_html) print(f在第 {page} 页找到 {len(book_urls)} 本书。) for book_url in book_urls: print(f 处理: {book_url}) detail_html self.fetch_page(book_url) if not detail_html: print(f 详情页获取失败跳过。) continue book_info self.parse_book_detail(detail_html, book_url) self.save_to_db(book_info) # 礼貌性延时避免请求过快 time.sleep(random.uniform(1, 3)) # 翻页延时 time.sleep(random.uniform(2, 4)) print(\n爬取任务完成) # 使用爬虫 if __name__ __main__: # 替换成你要爬的网站基础URL spider BookSpider(base_urlhttps://example-books.com) spider.crawl(start_page1, end_page2) # 爬取前2页6.2 代码结构与设计思路解析这个脚本采用了简单的面向对象设计将功能封装在BookSpider类中好处是结构清晰状态如base_url,headers易于管理也方便未来扩展。__init__初始化设置基础URL、请求头并初始化数据库。fetch_page请求模块专门负责发送HTTP请求并处理异常返回HTML字符串或None。这是为了将网络IO逻辑隔离让主流程更清晰。parse_book_list与parse_book_detail解析模块分别处理列表页和详情页。这是爬虫的核心逻辑高度依赖于目标网站的具体结构。这里也是未来网站改版后最需要修改的地方。save_to_db存储模块负责将一条条数据持久化到SQLite。使用了INSERT OR IGNORE来去重。crawl调度模块控制整个爬取流程串联起“获取列表 - 提取链接 - 获取详情 - 解析详情 - 保存数据”的循环并加入了随机延时。if __name__ __main__这是Python脚本的标准写法意味着当这个文件被直接运行时下面的代码才会执行。如果它被其他文件作为模块导入则不会执行。这保证了脚本的复用性。优化点提示错误恢复当前脚本遇到错误如请求失败会跳过当前项目。对于更健壮的爬虫可以考虑加入重试机制如对失败请求重试3次。增量爬取我们的去重依赖于数据库的唯一约束。更复杂的增量爬取可能需要对比内容哈希或更新时间。配置化可以将headers、请求延时范围、数据库路径等参数提取到配置文件或类外部方便调整。7. 常见问题、反爬策略与进阶方向7.1 爬虫常见错误与排查清单即使按照步骤操作你也可能会遇到一些问题。下面是一个快速排查指南问题现象可能原因解决方案请求返回403/404错误1. 网站屏蔽了Python默认User-Agent。2. 网址拼写错误或页面不存在。3. 需要登录或具有其他访问限制。1. 检查并更换为真实的浏览器User-Agent。2. 手动在浏览器访问该URL确认。3. 检查是否需要Cookie或Session见下文。获取到的中文是乱码网页编码与requests推断的编码不一致。1. 查看网页源码meta charset...标签确定编码。2. 手动设置response.encoding gbk或utf-8。BeautifulSoup找不到元素1. 元素是JavaScript动态加载的初始HTML中没有。2. 选择器写错了类名、标签名、结构。3. 网站结构已更新。1. 在开发者工具“Network”中查找XHR/Fetch请求尝试直接请求数据接口API。2. 使用浏览器检查元素核对选择器路径。3. 重新分析网页结构。数据保存到CSV后Excel打开乱码Excel默认可能不是UTF-8编码。保存CSV时使用encodingutf-8-sig。插入数据库时报错如重复键1. 违反了UNIQUE约束。2. 数据类型不匹配如字符串插入了数字字段。1. 使用INSERT OR IGNORE或先查询是否存在。2. 确保插入的数据类型与表结构定义一致。爬取速度很慢1. 网络延迟。2. 没有使用并发。3. 延时设置过长。1. 网络问题无法避免。2. 对于大量页面可学习concurrent.futures或aiohttp进行并发请求新手慎用。3. 适当调整延时在礼貌性和效率间平衡。7.2 应对基础反爬策略当你的爬虫开始规律地访问网站可能会触发一些基本的反爬机制。请求头Headers校验这是最低级的防护。除了User-Agent有些网站还会检查Referer来源页、Accept-Language接受语言等。你可以在浏览器开发者工具的“Network”标签里复制整个请求头信息直接用作headers字典。headers { User-Agent: ..., Referer: https://example.com/, Accept-Language: zh-CN,zh;q0.9,en;q0.8, Accept-Encoding: gzip, deflate, br, # 注意requests自动处理压缩不要在这里设置 Accept: text/html,application/xhtmlxml,application/xml;q0.9,*/*;q0.8 }Cookie与Session有些网站需要登录后才能访问或者利用Cookie跟踪会话。你可以手动复制登录后从浏览器开发者工具中复制Cookie字符串将其添加到headers中。使用requests.Session()Session对象可以自动处理Cookie像浏览器一样保持登录状态。session requests.Session() # 先模拟登录如果需要 login_data {username: your_user, password: your_pass} session.post(login_url, datalogin_data) # 后续请求都用这个session html session.get(target_url, headersheaders).textIP限制与代理如果同一个IP在短时间内发送过多请求可能会被暂时封禁。解决方案是使用代理IP池。但对于初学者和轻度爬取更实际的做法是严格遵守爬取礼仪加大请求间隔并尽量在网站访问低峰期如深夜运行脚本。重要提示务必遵守法律法规和网站的robots.txt协议。不要对网站造成过大负担每秒请求数控制在个位数。爬取的数据仅用于个人学习或分析切勿用于商业用途或侵犯他人权益。这是每个爬虫开发者应有的职业道德和法律底线。7.3 项目进阶方向当你掌握了基础爬虫后可以尝试以下方向来提升你的技能树动态内容爬取很多现代网站如单页应用SPA的数据是通过JavaScript异步加载的。requestsBeautifulSoup无法获取这些内容。这时需要用到Selenium或Playwright这类浏览器自动化工具它们可以驱动真实的浏览器如Chrome渲染页面再获取完整的HTML。它们的优点是能处理任何JS渲染的内容缺点是速度慢、资源消耗大。Scrapy框架对于大型、复杂的爬虫项目手动管理请求、解析、管道、去重会非常繁琐。Scrapy是一个专业的、异步的爬虫框架它提供了项目结构、中间件、管道、调度器等一套完整机制让开发大规模爬虫变得井井有条。它是企业级爬虫项目的首选。数据清洗与分析爬取数据只是第一步。你可以结合pandas库对数据进行清洗、转换和分析用matplotlib或seaborn进行可视化真正从数据中挖掘出价值。定时任务与部署让爬虫自动定期运行。可以在本地使用系统的定时任务如Linux的cronWindows的“任务计划程序”或者部署到云服务器上。对于更复杂的调度可以学习Celery等分布式任务队列。从“零基础”到能写出一个稳定运行的小爬虫你已经迈出了坚实的一步。爬虫技术的核心在于对网络协议HTTP/HTTPS和文档结构HTML/DOM的理解以及耐心细致的调试。剩下的就是在不断的实战中去面对千变万化的网页结构积累属于你自己的“解析经验库”。记住遇到问题多查文档requests,BeautifulSoup官方文档非常友好、多利用开发者工具分析、善用搜索引擎你解决问题的能力会在这个过程中飞速成长。本文还有配套的精品资源点击获取
返回列表