Python中如何结合Scrapy与Selenium解决爬虫问题?

事情是这样的我在用 scrapy 根据关键词爬取新浪主页网站前 30 页内容,只能模拟浏览器点击下一页爬取,根据官方

文档把 selenium 相关操作写进了 middlewares.py 中,现在的问题是 middleware 获取到 response 传给

spider 解析。 现在的问题是,spider 只能解析 第一页,其后的页面 无法传个 spider 解析,代码如下:

middleware.py

class SeleniumMiddleware(object):

def process_request(self,request,spider):
if spider_name == 'sina':
	driver = webdriver.Chrome()
    driver.get(request.url)
    next_page = driver.find_element_by_xpath('//a[contains(text(),'下一页’)]")
    next_page.click()
return HtmlResponse(request.url, body=driver.page_source,request=requst)

esle: return


Spier.py

class Sina(scrapy.Spider):

def start_request(self):
	keyword_list = ['a','b','c']
    max_page = 30
    for k in keyword_list:
    	for p in range(1,max_page+1):
        	url = base_url.format(k)
            yield scrapy.Request(url=url, callback=self.parse)

点击下一页 url 不会变动,我现在需要获取每条内容所在的页码,但是每次只能解析第一页就终止,希望大家指点一下


Python中如何结合Scrapy与Selenium解决爬虫问题?

8 回复

有可能是你第一页解析的时候出现错误导致无法进行下面的请求


在Scrapy里整合Selenium主要是为了处理那些依赖JavaScript渲染的动态页面。核心思路是用Selenium的WebDriver替换Scrapy默认的下载器中间件,让页面先经过浏览器渲染再交给爬虫解析。

具体操作分三步走:

  1. 安装依赖pip install selenium,并下载对应浏览器的WebDriver(比如ChromeDriver)。

  2. 创建自定义下载器中间件:这是关键。在这个中间件里,用Selenium加载请求URL,等页面渲染完,把生成的HTML返回给Spider。

  3. 在Spider里正常写解析逻辑:因为中间件已经处理了渲染,你拿到的response对象和普通静态页面一样,直接用XPath或CSS选择器解析就行。

下面是一个完整示例,爬取一个需要JS渲染的页面:

# middlewares.py
from scrapy.http import HtmlResponse
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
import time

class SeleniumMiddleware:
    def __init__(self):
        chrome_options = Options()
        chrome_options.add_argument('--headless')  # 无头模式,不显示浏览器窗口
        self.driver = webdriver.Chrome(options=chrome_options)

    def process_request(self, request, spider):
        if request.meta.get('selenium'):  # 通过meta字段控制哪些请求用Selenium
            self.driver.get(request.url)
            time.sleep(2)  # 等待JS加载,根据实际情况调整
            body = self.driver.page_source
            return HtmlResponse(self.driver.current_url, body=body, encoding='utf-8', request=request)

    def spider_closed(self):
        self.driver.quit()

# settings.py
DOWNLOADER_MIDDLEWARES = {
    'your_project.middlewares.SeleniumMiddleware': 543,
}

# your_spider.py
import scrapy

class DynamicSpider(scrapy.Spider):
    name = 'dynamic'
    
    def start_requests(self):
        url = 'https://example.com/dynamic-page'
        yield scrapy.Request(url, meta={'selenium': True})  # 启用Selenium
        
    def parse(self, response):
        # 现在response包含JS渲染后的完整HTML
        data = response.xpath('//div[@class="dynamic-content"]/text()').get()
        yield {'data': data}

注意几个点

  • meta={'selenium': True}来标记哪些请求需要Selenium处理,避免所有请求都走浏览器,拖慢速度。
  • time.sleep()是最简单的等待方式,但在实际项目中建议用Selenium的WebDriverWait配合expected_conditions做智能等待。
  • 记得在Spider关闭时退出WebDriver,释放资源。

总结:用中间件把Selenium嵌进Scrapy的请求流程里,就能兼顾Scrapy的并发优势和Selenium的渲染能力。

driver = webdriver.Chrome() 应该放在初始化的吧,process_request 里面生命周期

另外如果 xpath 没错的话, 还要 wait 跳页结束才行.HtmlResponse 里面的 request.url 也应该是 driver.current_url 吧

试过了 request.url 和 driver.current_url 效果一样, 我的疑问是 spider 是不是只能解析 中间件发回的第一个 response, 比如我想要前 30 页的如果我把结果拼接然后回传给 spider 是可以的,但是这样我就获取不到 内容对应的页码了

你这样写等于每一个请求都初始化一个 dirver,点击下一页,然后什么都不做,当然只解析第一页了啊,而且是解析了 30 次第一页

你这个 driver.page_source 获取方式错误了,这个只能获取页面 html 源码,你点击下一页,需要获取的是 DOM 结构,页面源码和 DOM 结构是不一样的。

你这样每次都重新打开一次新的浏览器页面。可以通过再 Request 中设置 meta 信息。在 middlewares 中判断。在第一次请求时打开一次网页。再次请求时,直接执行浏览器模拟。

回到顶部