【问题标题】:Scrapy with selenium for a webpage requiring authentication使用 selenium 抓取需要身份验证的网页
【发布时间】:2025-11-23 03:55:01
【问题描述】:

我正在尝试从具有大量 AJAX 调用和 javascript 执行的页面中抓取数据以呈现网页。所以我正在尝试使用带有 selenium 的 scrapy 来执行此操作。作案手法如下:

  1. 将登录页面URL添加到scrapy start_urls列表中

  2. 使用 formrequest from response 方法发布用户名和密码以进行身份​​验证。

  3. 登录后,请求抓取所需页面
  4. 将此响应传递给 Selenium Webdriver 以单击页面上的按钮。
  5. 单击按钮并呈现新网页后,捕获结果。

我目前的代码如下:

 from scrapy.spider import BaseSpider
 from scrapy.http import FormRequest, Request
 from selenium import webdriver
 import time


 class LoginSpider(BaseSpider):
    name = "sel_spid"
    start_urls = ["http://www.example.com/login.aspx"]


    def __init__(self):
        self.driver = webdriver.Firefox()


    def parse(self, response):
        return FormRequest.from_response(response,
               formdata={'User': 'username', 'Pass': 'password'},
               callback=self.check_login_response)

    def check_login_response(self, response):
        if "Log Out" in response.body:
            self.log("Successfully logged in")
            scrape_url = "http://www.example.com/authen_handler.aspx?SearchString=DWT+%3E%3d+500"
            yield Request(url=scrape_url, callback=self.parse_page)
        else:
            self.log("Bad credentials")

    def parse_page(self, response):
        self.driver.get(response.url)
        next = self.driver.find_element_by_class_name('dxWeb_pNext')
        next.click()
        time.sleep(2)
        # capture the html and store in a file

到目前为止,我遇到的两个障碍是:

  1. 第4步不起作用。每当selenium打开firefox窗口时,它总是在登录屏幕上,不知道如何过去。

  2. 不知道怎么实现第5步

任何帮助将不胜感激

【问题讨论】:

  • 理论上,您可以使用add_cookie方法将scrapy响应cookie传递给驱动程序,请参见:*.com/questions/16563073/…*.com/questions/19082248/…。不过,为什么不按照 Eric 的建议使用 selenium 登录呢?谢谢。
  • 我可以这样做,但我不想失去在scrapy引擎盖下运行的令人敬畏的扭曲代码。我计划在通过身份验证后抓取大量 URL,并希望这样做它以非阻塞方式..我的想法错了吗??

标签: python selenium scrapy


【解决方案1】:

我不相信你可以像这样在 scrapy Requests 和 selenium 之间切换。您需要使用 selenium 登录站点,而不是 yield Request()。您使用 scrapy 创建的登录会话不会转移到 selenium 会话。这是一个示例(元素 ids/xpath 会因您而异):

    scrape_url = "http://www.example.com/authen_handler.aspx"
    driver.get(scrape_url)
    time.sleep(2)
    username = self.driver.find_element_by_id("User")
    password =  self.driver.find_element_by_name("Pass")
    username.send_keys("your_username")
    password.send_keys("your_password")
    self.driver.find_element_by_xpath("//input[@name='commit']").click()

那么你可以这样做:

    time.sleep(2)
    next = self.driver.find_element_by_class_name('dxWeb_pNext').click()
    time.sleep(2)

等等。

编辑:如果您需要渲染 javascript 并且担心速度/非阻塞,您可以使用 http://splash.readthedocs.org/en/latest/index.html 来解决问题。

http://splash.readthedocs.org/en/latest/scripting-ref.html#splash-add-cookie有传递cookie的详细信息,你应该可以从scrapy传递它,但我之前没有做过。

【讨论】:

    【解决方案2】:

    先用scrapy api登录

    # call scrapy post request with after_login as callback
        return FormRequest.from_response(
            response,
            # formxpath=formxpath,
            formdata=formdata,
            callback=self.browse_files
        )
    

    将会话传递给 selenium chrome 驱动程序

    # logged in previously with scrapy api   
    def browse_files(self, response):
        print "browse files for: %s" % (response.url)
    
        # response.headers        
        cookie_list2 = response.headers.getlist('Set-Cookie')
        print cookie_list2
    
        self.driver.get(response.url)
        self.driver.delete_all_cookies()
    
        # extract all the cookies
        for cookie2 in cookie_list2:
            cookies = map(lambda e: e.strip(), cookie2.split(";"))
    
            for cookie in cookies:
                splitted = cookie.split("=")
                if len(splitted) == 2:
                    name = splitted[0]
                    value = splitted[1]
                    #for my particular usecase I needed only these values
                    if name == 'csrftoken' or name == 'sessionid':
                        cookie_map = {"name": name, "value": value}
                    else:
                        continue
                elif len(splitted) == 1:
                    cookie_map = {"name": splitted[0], "value": ''}
                else:
                    continue
    
                print "adding cookie"
                print cookie_map
                self.driver.add_cookie(cookie_map)
    
        self.driver.get(response.url)
    
        # check if we have successfully logged in
        files = self.wait_for_elements_to_be_present(By.XPATH, "//*[@id='files']", response)
        print files
    

    【讨论】:

    • 为什么要给self.driver.get(response.url)打两次电话?
    • 如果我没记错的话,是强制加载动态内容。
    最近更新 更多