Python爬虫实战:突破验证码与登录,高效采集药师帮药品数据

1. 项目概述:从“药师帮”看数据采集的实战价值

最近在整理医药行业的数据,发现“药师帮”这个平台的信息非常关键。它作为国内领先的医药B2B平台,汇聚了海量的药品信息、价格动态和供应商数据,对于市场分析、竞品调研乃至学术研究都有不小的参考价值。不过,手动去一个个页面复制粘贴显然不现实,这时候,用Python写个爬虫就成了最直接高效的解决方案。这个项目,就是要搞定药师帮网站的数据抓取,把我们需要的信息结构化地保存下来。

听起来好像就是个普通的爬虫,但做过的人都知道,这类成熟的商业平台往往防护严密,登录验证、动态加载、反爬机制一个不少。特别是验证码,几乎是绕不开的坎。所以,这次我们不只讲怎么发请求、解析HTML,更要重点解决如何突破这些常见的访问障碍,尤其是验证码识别和模拟登录的问题。最终目标是把爬取到的数据,比如药品名称、规格、厂家、价格、库存等,规整地存进CSV文件里,方便后续用Excel或Pandas做分析。

无论你是想学习如何处理复杂的登录场景,还是需要获取特定领域的商业数据,这个案例都能提供一套完整的、可复现的思路和代码。我们会用到requestsselenium这些库,也会探讨一些应对反爬的策略。下面,我就把这次实战中的关键步骤、踩过的坑以及解决方案,详细拆解一遍。

2. 核心需求解析与技术选型

2.1 目标网站分析与核心挑战

药师帮(yaoshang.com)是一个需要登录才能访问大部分内容的平台,这直接决定了我们爬虫的基本形态:必须是一个能模拟用户登录行为的“浏览器”。经过初步探查,其核心挑战主要集中在以下几点:

  1. 登录与会话维持:网站采用账号密码登录,成功后依赖Cookies或Session来维持登录状态。爬虫必须能成功完成登录流程,并妥善管理这些凭证,以便后续的页面抓取。
  2. 验证码识别:登录或频繁访问时,极大概率会触发验证码。可能是常见的数字字母图片验证码,也可能是更复杂的滑块、点选等交互式验证码。这是本项目需要攻克的首要技术难点。
  3. 动态内容加载:药品列表、详情信息很可能不是一次性加载在初始HTML中的,而是通过JavaScript发起Ajax请求,从后端接口动态获取JSON数据。这意味着简单的requests.get()可能拿不到完整数据。
  4. 反爬虫机制:除了验证码,网站可能还设有请求头校验、访问频率限制、IP封禁等常规反爬措施。爬虫需要表现得像一个“好人”。
  5. 数据结构化提取与存储:最终我们需要从复杂的HTML或JSON中,精准提取出目标字段,并以表格形式(如CSV)持久化存储,保证数据的整洁和可用性。

2.2 技术栈选型与理由

针对上述挑战,我选择了以下技术组合,并解释一下为什么这么选:

  • 核心请求库:Requests+Selenium双引擎

    • Requests:轻量级,效率高,用于处理那些不需要执行JS的简单请求,例如提交登录表单(如果验证码能解决)、访问静态接口。它是我们获取数据的主力。
    • Selenium:浏览器自动化工具。当网站内容严重依赖JavaScript渲染,或者验证码交互复杂到必须用真实浏览器环境触发时,Selenium就派上用场了。我们可以用它来打开浏览器,完成登录(包括处理验证码),获取渲染后的页面源码或Cookies,再交给Requests去高效爬取。这是一种“混合模式”。
    • 为什么不只用Selenium?因为Selenium驱动真实浏览器,资源消耗大、速度慢,不适合大规模数据抓取。它更适合完成“打开门”这个动作。
    • 为什么不只用Requests?因为对于动态加载和复杂验证码,纯Requests模拟的难度和成本可能更高。
  • 验证码处理:ddddocr/Selenium手动过码 / 打码平台

    • ddddocr:一个开源且效果不错的通用验证码识别库。对于简单的数字字母图片验证码,可以尝试用它本地识别,成本最低。
    • Selenium手动过码:在开发调试阶段,或者验证码过于复杂时,最稳妥的方法是让程序在出现验证码时暂停,我们手动输入,然后程序继续。这保证了登录的成功率。
    • 第三方打码平台:如果项目需要全自动化且验证码识别率要求高,可以接入专业的打码平台API。这会产生费用,但稳定性和识别率有保障。本项目为演示原理,优先采用前两种方式。
  • 数据解析:BeautifulSoup4/lxml/ 直接处理JSON

    • BeautifulSoup4:HTML解析神器,语法友好,适合从复杂的HTML标签树中提取数据。
    • 如果数据直接通过Ajax接口返回为JSON格式,那最简单,直接用json()方法解析即可,效率最高。
  • 数据存储:csv模块

    • Python标准库中的csv模块,简单可靠,足以将提取出的数据列表写入到CSV文件中。CSV格式通用,便于用Excel、Numbers或Pandas进行下一步分析。
  • 其他辅助工具

    • time/random:用于在请求间插入随机延时,模拟真人操作,避免因请求过快被封。
    • Fake-Useragent:用于生成随机的、真实的浏览器User-Agent字符串,伪装请求头。

注意:在开始任何爬虫项目前,请务必仔细阅读目标网站的robots.txt文件和服务条款,尊重网站的版权和数据所有权,合理控制爬取频率,避免对目标网站服务器造成过大压力。本案例仅用于技术学习与交流。

3. 环境准备与核心工具配置

3.1 Python环境与库安装

首先确保你有一个Python 3.7以上的环境。然后,我们通过pip安装所需的第三方库。建议创建一个虚拟环境来管理依赖。

# 安装核心请求与解析库 pip install requests selenium beautifulsoup4 # 安装验证码识别库 (可选,用于简单图形验证码) pip install ddddocr # 安装随机User-Agent生成库 pip install fake-useragent # 安装用于解析动态JSON路径的库 (可选,如果数据在复杂的JSON中) # pip install jsonpath-ng

3.2 Selenium与浏览器驱动配置

Selenium需要对应的浏览器驱动才能工作。这里以最常用的Chrome浏览器为例。

  1. 查看Chrome版本:打开Chrome,在地址栏输入chrome://version/,查看“Google Chrome”后面的版本号(例如,120.0.6099.110)。
  2. 下载ChromeDriver:访问 ChromeDriver官网 或国内镜像站,下载与你的Chrome浏览器主版本号完全相同的驱动(例如,Chrome 120.x就找120.x.x.x版本的驱动)。
  3. 放置驱动:将下载的chromedriver.exe(Windows)或chromedriver(Mac/Linux)文件放在一个目录下,并将该目录添加到系统的PATH环境变量中。更简单的方法是,直接把它放在Python的安装目录下(和python.exe同级),或者放在项目根目录,然后在代码中指定其路径。

一个更省事的方法是使用webdriver-manager库,它可以自动下载和管理匹配的浏览器驱动。

pip install webdriver-manager

然后在代码中这样使用:

from selenium import webdriver from selenium.webdriver.chrome.service import Service from webdriver_manager.chrome import ChromeDriverManager service = Service(ChromeDriverManager().install()) driver = webdriver.Chrome(service=service)

3.3 项目结构设计

在开始编码前,规划一个清晰的项目结构有助于代码管理。建议如下:

pharmacist-helper-spider/ ├── config.py # 配置文件,存放URL、账号、密码等敏感信息 ├── main.py # 主程序入口 ├── core/ │ ├── login.py # 登录模块,处理验证码和会话获取 │ ├── spider.py # 核心爬取逻辑 │ └── utils.py # 工具函数,如请求头生成、延时处理 ├── data/ # 存放爬取到的CSV数据 └── logs/ # 存放日志文件(可选)

将账号密码等敏感信息放在config.py中,并用.gitignore忽略它,避免泄露。

4. 核心环节一:模拟登录与验证码攻克

登录是获取数据权限的第一步,也是最容易卡住的一步。我们设计一个稳健的登录流程。

4.1 手动分析登录流程

首先,手动在浏览器中打开药师帮登录页,用开发者工具(F12)的“网络”(Network)选项卡监控登录过程。

  1. 找到登录表单提交的URL(通常是/login/api/login这样的接口)。
  2. 查看提交的请求方法(POST)和请求体(Form Data或Payload),里面通常包含usernamepasswordcaptcha(验证码)等字段,还可能有一个隐藏的csrf_token
  3. 查看响应,登录成功后是跳转还是返回一个包含token/session的JSON。

假设我们分析发现,登录接口是https://www.yaoshang.com/api/login,POST方法,需要phonepasswordcaptcha三个参数。

4.2 实现登录模块

我们创建一个login.py文件,核心任务是获取一个有效的登录会话(Requests的Session对象)。

方案A:使用Selenium处理复杂登录(推荐用于绕过复杂验证码)

from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC import time from config import USERNAME, PASSWORD def login_with_selenium(): """ 使用Selenium模拟浏览器登录,获取Cookies。 此方法能处理任何在浏览器中可见的登录交互,包括复杂验证码。 """ options = webdriver.ChromeOptions() # 可选:无头模式,不显示浏览器窗口 # options.add_argument('--headless') # 可选:禁用一些自动化特征,降低被检测风险 options.add_argument('--disable-blink-features=AutomationControlled') options.add_experimental_option('excludeSwitches', ['enable-automation']) options.add_experimental_option('useAutomationExtension', False) driver = webdriver.Chrome(options=options) driver.execute_cdp_cmd('Page.addScriptToEvaluateOnNewDocument', { 'source': 'Object.defineProperty(navigator, \"webdriver\", {get: () => undefined})' }) try: driver.get('https://www.yaoshang.com/login') # 替换为实际登录页URL wait = WebDriverWait(driver, 10) # 1. 定位并输入用户名 username_input = wait.until(EC.presence_of_element_located((By.NAME, 'phone'))) # 根据实际元素名修改 username_input.send_keys(USERNAME) time.sleep(1) # 模拟人操作间隔 # 2. 定位并输入密码 password_input = driver.find_element(By.NAME, 'password') password_input.send_keys(PASSWORD) time.sleep(1) # 3. 处理验证码 - 这里是关键! # 情况1:简单图片验证码,可以尝试自动识别 # captcha_element = driver.find_element(By.XPATH, '//img[@class="captcha-img"]') # captcha_src = captcha_element.get_attribute('src') # # 下载图片并用ddddocr识别(此处省略识别代码) # # captcha_text = recognize_captcha(captcha_src) # # driver.find_element(By.NAME, 'captcha').send_keys(captcha_text) # 情况2:复杂验证码或自动识别失败,采用手动输入(开发调试用) print("【请手动完成验证码验证,完成后在控制台按回车继续...】") input() # 程序暂停,等待用户手动输入验证码并点击登录后,按回车 # 4. 点击登录按钮 login_button = driver.find_element(By.XPATH, '//button[@type="submit"]') login_button.click() time.sleep(3) # 等待登录完成和页面跳转 # 5. 检查是否登录成功(例如,检查是否跳转到首页或出现用户菜单) # if 'dashboard' in driver.current_url: # print("登录成功!") # else: # print("登录可能失败,请检查。") # 6. 获取登录后的Cookies,这是最关键的一步! cookies = driver.get_cookies() # 将Selenium格式的cookies转换为Requests可用的字典格式 cookies_dict = {cookie['name']: cookie['value'] for cookie in cookies} print(f"成功获取Cookies: {list(cookies_dict.keys())}") return cookies_dict except Exception as e: print(f"登录过程中出现错误: {e}") return None finally: driver.quit() # 关闭浏览器,释放资源 # 将获取的cookies用于创建Requests Session import requests def create_session_from_cookies(cookies_dict): session = requests.Session() # 将cookies添加到session中 for name, value in cookies_dict.items(): session.cookies.set(name, value) # 更新session的请求头,使其更像浏览器 session.headers.update({ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ...', 'Referer': 'https://www.yaoshang.com/', 'Accept-Language': 'zh-CN,zh;q=0.9', }) return session

方案B:纯Requests登录(如果验证码可破解)

如果验证码是简单的图片,且你能找到获取验证码图片的独立接口,可以尝试纯Requests方案。

import requests from ddddocr import DdddOcr from config import LOGIN_URL, USERNAME, PASSWORD def login_with_requests(): session = requests.Session() # 1. 首先访问登录页,获取可能需要的token或初始cookies resp_init = session.get('https://www.yaoshang.com/login') # 可能需要从resp_init.text中解析出csrf_token (使用BeautifulSoup) # 2. 获取验证码图片 # 假设验证码图片URL是固定的,或者通过某个接口获取 captcha_url = 'https://www.yaoshang.com/api/captcha' captcha_resp = session.get(captcha_url) with open('captcha.png', 'wb') as f: f.write(captcha_resp.content) # 3. 识别验证码 ocr = DdddOcr() with open('captcha.png', 'rb') as f: image_bytes = f.read() captcha_code = ocr.classification(image_bytes) print(f"识别出的验证码为: {captcha_code}") # 4. 构造登录数据 login_data = { 'phone': USERNAME, 'password': PASSWORD, 'captcha': captcha_code, # 'csrf_token': csrf_token, # 如果有的话 } # 5. 提交登录 login_resp = session.post(LOGIN_URL, data=login_data) # 检查响应,判断是否登录成功 if login_resp.status_code == 200 and 'success' in login_resp.text: print("Requests登录成功!") return session else: print(f"登录失败,响应: {login_resp.text[:200]}") return None

实操心得:对于药师帮这类商业平台,方案A(Selenium获取Cookies + Requests爬取)的稳定性远高于方案B。手动过验证码虽然需要人工干预,但在数据量不是特别巨大的情况下,登录一次获取的Cookies可能能维持一段时间(如几小时)的会话,足够完成一次爬取任务。这是性价比最高的方式。全自动识别验证码的投入产出比在初期往往不高。

5. 核心环节二:页面爬取与数据解析策略

成功登录并获得有效Session后,就可以开始爬取目标数据了。我们需要分析数据在页面中是如何呈现的。

5.1 分析数据加载方式

打开药师帮的商品列表页或搜索页,同样使用开发者工具的“网络”选项卡。

  1. 观察请求:滚动页面时,是否出现了新的XHR/Fetch请求?这些请求的URL和响应内容(通常是JSON)很可能包含了我们需要的结构化数据。这是最理想的情况,因为JSON数据无需解析HTML,直接提取即可。
  2. 检查响应:如果滚动没有新请求,数据可能直接渲染在初始HTML中。这时就需要用BeautifulSoup来解析HTML。

假设我们发现了一个搜索接口:GET https://www.yaoshang.com/api/search?keyword=阿莫西林&page=1,返回JSON数据。

5.2 实现核心爬取逻辑

我们创建spider.py,编写爬取和解析函数。

import requests import time import random from bs4 import BeautifulSoup import json from urllib.parse import quote from core.utils import get_random_ua # 假设有一个生成随机User-Agent的工具函数 class YaoshangSpider: def __init__(self, session): """ 初始化爬虫,传入已登录的requests.Session对象。 """ self.session = session self.base_url = 'https://www.yaoshang.com' self.data_list = [] # 用于存储爬取到的所有数据 def fetch_via_api(self, keyword, max_pages=5): """ 通过分析出的API接口爬取数据。 :param keyword: 搜索关键词 :param max_pages: 最大爬取页数 """ for page in range(1, max_pages + 1): print(f"正在爬取关键词『{keyword}』第 {page} 页...") # 构造API URL,注意关键词需要URL编码 encoded_keyword = quote(keyword) api_url = f'{self.base_url}/api/search?keyword={encoded_keyword}&page={page}&size=20' try: # 使用已有的登录session发送请求 resp = self.session.get(api_url, timeout=10) resp.raise_for_status() # 如果状态码不是200,抛出异常 # 解析JSON响应 data = resp.json() # 根据实际JSON结构提取商品列表,例如 data['list'] items = data.get('list', []) if not items: print(f"第 {page} 页无数据,可能已到末页。") break for item in items: # 提取所需字段,这里字段名是假设的,需要根据实际响应调整 product_info = { '商品ID': item.get('productId'), '商品名称': item.get('productName'), '通用名': item.get('commonName'), '规格': item.get('spec'), '厂家': item.get('manufacturer'), '参考价格': item.get('price'), '库存状态': item.get('stockStatus'), '最小起批量': item.get('minOrder'), '供应商': item.get('supplierName'), '爬取时间': time.strftime('%Y-%m-%d %H:%M:%S') } self.data_list.append(product_info) print(f" 已添加: {product_info['商品名称'][:20]}...") # 随机延时,模拟人工操作,避免被封 time.sleep(random.uniform(1.5, 3.5)) # 检查是否还有下一页(根据接口返回的字段判断,如 data['hasNext']) # if not data.get('hasNext', True): # break except requests.exceptions.RequestException as e: print(f"请求第 {page} 页时发生错误: {e}") break except json.JSONDecodeError as e: print(f"解析第 {page} 页JSON时发生错误: {e}") print(f"响应文本: {resp.text[:500]}") break def fetch_via_html(self, start_url): """ 如果需要从HTML页面解析数据(当没有直接API时)。 """ resp = self.session.get(start_url) soup = BeautifulSoup(resp.text, 'lxml') # 使用BeautifulSoup根据实际HTML结构定位商品元素 # 例如,商品项可能在 <div class="product-item"> 里 product_items = soup.find_all('div', class_='product-item') for item in product_items: # 提取信息,这里的选择器是示例,必须根据实际网站调整 name_elem = item.find('a', class_='product-name') price_elem = item.find('span', class_='price') # ... 其他字段 product_info = { '商品名称': name_elem.text.strip() if name_elem else '', '价格': price_elem.text.strip() if price_elem else '', # ... } self.data_list.append(product_info) # 查找下一页链接(如果有) # next_page = soup.find('a', text='下一页') # if next_page: # next_url = self.base_url + next_page['href'] # self.fetch_via_html(next_url) # 递归或循环爬取 def get_data(self): """返回爬取到的所有数据。""" return self.data_list

5.3 应对反爬策略

  1. 请求头伪装:确保Session的headers包含User-Agent,Referer,Accept-Language等常见浏览器头。使用fake-useragent库轮换User-Agent
  2. 请求频率控制:在每次请求后使用time.sleep(random.uniform(a, b))进行随机延时。对于重要网站,建议将b设置为3秒以上。
  3. IP代理池:如果爬取量非常大或触发了IP限制,需要考虑使用代理IP。可以购买付费代理服务或自建代理池。在requests中通过proxies参数使用。
    proxies = { 'http': 'http://your-proxy-ip:port', 'https': 'http://your-proxy-ip:port', } resp = session.get(url, proxies=proxies)
  4. 错误重试机制:对于网络超时等临时性错误,可以编写重试逻辑。
    import tenacity # 一个很好的重试库 @tenacity.retry(stop=tenacity.stop_after_attempt(3), wait=tenacity.wait_fixed(2)) def safe_request(url): return session.get(url)

6. 核心环节三:数据存储与CSV导出

爬取到的数据存储在内存的列表里,最后需要持久化到硬盘。CSV是最简单通用的选择。

import csv import os from datetime import datetime class DataExporter: @staticmethod def save_to_csv(data_list, filename_prefix='yaoshang_data'): """ 将数据列表保存为CSV文件。 :param data_list: 列表,其中每个元素是一个字典 :param filename_prefix: 文件名前缀 """ if not data_list: print("没有数据可保存。") return # 生成带时间戳的文件名,避免覆盖 timestamp = datetime.now().strftime('%Y%m%d_%H%M%S') filename = f'data/{filename_prefix}_{timestamp}.csv' os.makedirs('data', exist_ok=True) # 确保data目录存在 # 获取字典的所有键作为CSV的表头 fieldnames = data_list[0].keys() try: with open(filename, 'w', newline='', encoding='utf-8-sig') as csvfile: # utf-8-sig支持Excel直接打开显示中文 writer = csv.DictWriter(csvfile, fieldnames=fieldnames) writer.writeheader() writer.writerows(data_list) print(f"数据已成功保存至: {filename}, 共 {len(data_list)} 条记录。") except IOError as e: print(f"写入CSV文件时出错: {e}") except Exception as e: print(f"保存数据时发生未知错误: {e}") @staticmethod def append_to_csv(data_list, filename='yaoshang_data_latest.csv'): """追加数据到现有CSV文件(如果文件存在且结构相同)。""" # 实现逻辑类似,区别在于打开模式用 'a',且如果文件不存在或为空时才写入表头。 pass

注意事项:使用utf-8-sig编码而不是utf-8,是为了在Windows系统的Excel中打开CSV时,中文字符能正常显示,而不会变成乱码。这是一个非常实用的小技巧。

7. 主程序串联与完整流程

最后,我们在main.py中将所有模块串联起来,形成一个完整的、可执行的流程。

from core.login import login_with_selenium, create_session_from_cookies from core.spider import YaoshangSpider from core.exporter import DataExporter import time def main(): print("=== 药师帮数据爬虫启动 ===") # 第一步:登录,获取有效Cookies print("\n1. 正在尝试登录...") cookies = login_with_selenium() # 这里会弹出浏览器,需要手动过验证码 if not cookies: print("登录失败,程序退出。") return # 第二步:用Cookies创建持久化会话 session = create_session_from_cookies(cookies) print("2. 登录会话创建成功。") # 第三步:初始化爬虫 spider = YaoshangSpider(session) # 第四步:执行爬取任务(可以爬取多个关键词) search_keywords = ['阿莫西林', '布洛芬', '连花清瘟'] # 示例关键词 for keyword in search_keywords: print(f"\n3. 开始爬取关键词: {keyword}") spider.fetch_via_api(keyword, max_pages=3) # 每个关键词爬3页 time.sleep(random.uniform(5, 10)) # 不同关键词间长间隔 # 第五步:获取并保存数据 all_data = spider.get_data() print(f"\n4. 爬取结束,共获取 {len(all_data)} 条商品数据。") if all_data: DataExporter.save_to_csv(all_data) print("5. 数据保存完成。") else: print("5. 未获取到任何数据。") print("\n=== 程序执行完毕 ===") if __name__ == '__main__': main()

8. 常见问题与排查技巧实录

在实际运行中,你几乎一定会遇到各种问题。下面是我踩过的一些坑和解决方法。

8.1 登录相关问题

  • 问题:Selenium打开的浏览器被检测到是自动化工具。

    • 现象:页面有提示,或者无法正常加载登录元素。
    • 解决:使用options.add_experimental_optionexecute_cdp_cmd来隐藏WebDriver特征(代码中已体现)。也可以尝试使用undetected-chromedriver这个第三方库,它专门用于规避检测。
  • 问题:手动输入验证码后,程序还是提示登录失败。

    • 排查
      1. 检查输入账号密码和点击登录按钮的代码,定位的元素是否正确(ID、Class、XPath可能随网站更新而变化)。用driver.save_screenshot('debug.png')截图看看页面状态。
      2. 检查登录成功后的判断条件。有时登录后不是直接跳转,而是弹出一个提示框。可以尝试等待某个只有登录后才出现的元素出现,如用户头像wait.until(EC.presence_of_element_located((By.CLASS_NAME, 'user-avatar')))
      3. 获取Cookies的时机可能太早。在点击登录后,用WebDriverWait显式等待几秒,确保登录流程完全走完再获取Cookies。
  • 问题:通过API登录(方案B)总是返回验证码错误。

    • 解决:这通常意味着验证码识别失败,或者网站有更复杂的校验(如一次性的token)。强烈建议放弃纯Requests方案,改用Selenium获取Cookies的方案A。这是绕过复杂登录验证最有效的方法。

8.2 爬取数据问题

  • 问题:session.get(api_url)返回的状态码是403或404。

    • 排查
      1. 403 Forbidden:最可能的原因是请求头不完整或被识别为爬虫。检查Session的headers,确保包含RefererOrigin等。尝试更新User-Agent
      2. 404 Not Found:API接口URL可能不对,或者需要特定的参数。再次用浏览器开发者工具仔细核对真实的请求URL和参数。
      3. Cookies失效:登录获取的Cookies可能有过期时间。如果爬取时间过长,中间可能需要重新登录。可以在代码中加入对特定错误响应(如跳转到登录页)的判断,触发重新登录流程。
  • 问题:API返回的数据是空的,但浏览器能看到数据。

    • 排查
      1. 检查请求参数是否齐全。有些接口需要pageSize,sortType,timestamp等额外参数。
      2. 检查请求方法。有些接口可能是POST而不是GET
      3. 尝试用Session直接访问一个在浏览器中能正常看到数据的完整URL,看看返回什么。可能是接口有鉴权,你的Session权限不足(比如只能看部分数据)。
  • 问题:爬了几页后就被封IP了。

    • 解决
      1. 首要措施:大幅增加请求间隔时间。将time.sleep的随机范围调大,例如random.uniform(5, 15)
      2. 使用代理IP:这是解决IP封锁的根本方法。搭建或购买代理IP池,在请求时轮换使用。
      3. 模拟更真实的行为:除了延时,还可以模拟鼠标移动、随机滚动页面(在Selenium中)等。

8.3 数据解析与存储问题

  • 问题:BeautifulSoup找不到元素,返回空列表。

    • 排查
      1. 确认页面已加载完成:如果是用Requests获取的HTML,可能页面内容是JS动态生成的。这时必须用Selenium获取渲染后的driver.page_source
      2. 检查选择器:网站的HTML结构可能已经改变。使用浏览器的“检查”功能,重新确认目标元素的选择器(XPath或CSS Selector)。优先使用相对稳定、有语义化的属性,如>