如何在scrapy中处理302重定向

Posted

技术标签:

【中文标题】如何在scrapy中处理302重定向【英文标题】:how to handle 302 redirect in scrapy 【发布时间】:2020-03-16 08:11:26 【问题描述】:

我在废弃网站时收到来自服务器的 302 响应:

2014-04-01 21:31:51+0200 [ahrefs-h] DEBUG: Redirecting (302) to <GET http://www.domain.com/Site_Abuse/DeadEnd.htm> from <GET http://domain.com/wps/showmodel.asp?Type=15&make=damc&a=664&b=51&c=0>

我想向 GET 网址发送请求,而不是被重定向。现在我找到了这个中间件:

https://github.com/scrapy/scrapy/blob/master/scrapy/contrib/downloadermiddleware/redirect.py#L31

我将此重定向代码添加到了我的 middleware.py 文件中,并将其添加到了 settings.py 中:

DOWNLOADER_MIDDLEWARES = 
 'street.middlewares.RandomUserAgentMiddleware': 400,
 'street.middlewares.RedirectMiddleware': 100,
 'scrapy.contrib.downloadermiddleware.useragent.UserAgentMiddleware': None,

但我仍然被重定向。为了让这个中间件工作,我要做的就是这样吗?我错过了什么吗?

【问题讨论】:

他们可能会无休止地重定向您,以防止您抓取该网站。至少,这就是 URL 让我相信的。 是的,这显然是他们的参加,也是我发布这个问题的原因。它不是一个无限循环,它是一个简单的 302 重定向,原始 url 仍然作为 GET:从 domain.com/wps/…> 接收,这就是我想要发送请求的 URL。据我所知,这是可能的,我找到了一个脚本,但由于某种原因我的设置不起作用。 我并不是说这是一个无限循环。我的意思是每次你提出请求时,你都会被重定向,所以他们拒绝给你内容。 响应头将返回 302 和正确的 url,你只需要删除 302 的一个,你必须取另一个,这正是你想要的……@987654323 @了解有关 302 标头的更多信息 【参考方案1】:

在这种情况下忘记了中间件,这可以解决问题:

meta = 'dont_redirect': True,'handle_httpstatus_list': [302]

也就是说,您需要在产生请求时包含元参数:

yield Request(item['link'],meta = 
                  'dont_redirect': True,
                  'handle_httpstatus_list': [302]
              , callback=self.your_callback)

【讨论】:

当前的 Scrapy 版本对我不起作用,我尝试使用 handle_httpstatus_list 中的其他代码,如 404,并且工作正常。它只是不适用于 301 和 302。有什么想法吗? @mrki 如何手动处理 start url 的重定向,意味着 start_urls 是否被重定向到其他地方? 'handle_httpstatus_list': [302] 在 scrapy==1.4.0 中工作【参考方案2】:

无法解释的302 响应,例如从在网络浏览器中加载良好的页面重定向到主页或某个固定页面,通常表示针对不受欢迎活动的服务器端措施。

您必须降低抓取速度或使用智能代理(例如Crawlera)或代理轮换服务,并在收到此类响应时重试您的请求。

要重试此类响应,请将'handle_httpstatus_list': [302] 添加到源请求的meta,并检查回调中是否有response.status == 302。如果是,请通过 yield response.request.replace(dont_filter=True) 重试您的请求。

重试时,您还应该让代码限制任何给定 URL 的最大重试次数。您可以保留一个字典来跟踪重试次数:

class MySpider(Spider):
    name = 'my_spider'

    max_retries = 2

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.retries = 

    def start_requests(self):
        yield Request(
            'https://example.com',
            callback=self.parse,
            meta=
                'handle_httpstatus_list': [302],
            ,
        )

    def parse(self, response):
        if response.status == 302:
            retries = self.retries.setdefault(response.url, 0)
            if retries < self.max_retries:
                self.retries[response.url] += 1
                yield response.request.replace(dont_filter=True)
            else:
                self.logger.error('%s still returns 302 responses after %s retries',
                                  response.url, retries)
            return

根据具体情况,您可能希望将此代码移至downloader middleware。

【讨论】:

【参考方案3】:

您可以通过在 settings.py 中将 REDIRECT_ENABLED 设置为 False 来禁用 RedirectMiddleware

【讨论】:

【参考方案4】:

我想出了如何通过以下方式绕过重定向:

1- 检查是否在 parse() 中重定向。

2- 如果被重定向,则安排模拟转义此重定向的操作并返回到您所需的 URL 进行抓取,您可能需要检查 google chrome 中的网络行为并模拟请求的 POST 以返回您的页面。

3- 进入另一个进程,使用回调,然后在这个进程内通过递归循环调用自身来完成所有的抓取工作,并在最后加上条件来打破这个循环。

下面的示例我曾经绕过免责声明页面并返回到我的主 url 并开始抓取。

from scrapy.http import FormRequest
import requests


class ScrapeClass(scrapy.Spider):

name = 'terrascan'

page_number = 0


start_urls = [
    Your MAin URL , Or list of your URLS, or Read URLs fro file to a list
              ]


def parse(self, response):

    ''' Here I killed Disclaimer page and continued in below proc with follow !!!'''

    # Get Currently Requested URL
    current_url = response.request.url

    # Get All Followed Redirect URLs
    redirect_url_list = response.request.meta.get('redirect_urls')
    # Get First URL Followed by Spiders
    redirect_url_list = response.request.meta.get('redirect_urls')[0]

    # handle redirection as below  ( check redirection !! , got it from redirect.py
    # in \downloadermiddlewares  Folder

    allowed_status = (301, 302, 303, 307, 308)
    if 'Location' in response.headers or response.status in allowed_status: # <== this is condition of redirection
        
        print(current_url, '<========= am not redirected @@@@@@@@@@')
    else:
       
        print(current_url, '<====== kill that please %%%%%%%%%%%%%')
        
        session_requests = requests.session()


        # got all below data from monitoring network behavior in google chrome when simulating clicking on 'I Agree'

        headers_ = 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:70.0) Gecko/20100101 Firefox/70.0',

                    'ctl00$cphContent$btnAgree': 'I Agree'
                    
        # headers_ = 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:70.0) Gecko/20100101 Firefox/70.0'

        # Post_ = session_requests.post(current_url, headers=headers_)
        Post_ = session_requests.post(current_url, headers=headers_)

        # if Post_.status_code == 200: print('heeeeeeeeeeeeeeeeeeeeeey killed it')

        print(response.url , '<========= check this please')



        return FormRequest.from_response(Post_,callback=self.parse_After_disclaimer)



def parse_After_disclaimer(self, response):

    print(response.status)
    print(response.url)

    # put your condition to make sure that the current url is what you need, other wise escape again until you kill redirection 

    if response.url not in [your lis of URLs]:
        print('I am here brother')
        yield scrapy.Request(Your URL,callback=self.parse_After_disclaimer)

    else:
      
        # here you are good to go for scraping work          
        items = TerrascanItem()

        all_td_tags = response.css('td')
        print(len(all_td_tags),'all_td_results',response.url)

        # for tr_ in all_tr_tags:
        parcel_No = all_td_tags.css('#ctl00_cphContent_ParcelOwnerInfo1_lbParcelNumber::text').extract()
        Owner_Name = all_td_tags.css('#ctl00_cphContent_ParcelOwnerInfo1_lbOwnerName::text').extract()

     
        if parcel_No:items['parcel_No'] = parcel_No
        else: items['parcel_No'] =''


        yield items

    # Here you put the condition to recursive call of this process again
    
    #
    ScrapeClass.page_number += 1
    # next_page = 'http://terrascan.whitmancounty.net/Taxsifter/Search/results.aspx?q=[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]&page=' + str(terraScanSpider.page_number) + '&1=1#rslts'
    next_page = Your URLS[ScrapeClass.page_number]
    print('am in page #', ScrapeClass.page_number, '===', next_page)
    if ScrapeClass.page_number < len(ScrapeClass.start_urls_AfterDisclaimer)-1:  # 20
        # print('I am loooooooooooooooooooooooping again')
        yield response.follow(next_page, callback=self.parse_After_disclaimer)

【讨论】:

【参考方案5】:

我将此重定向代码添加到了我的 middleware.py 文件中,并将其添加到了 settings.py 中:

DOWNLOADER_MIDDLEWARES_BASE 表示RedirectMiddleware 已经默认启用,所以你做了什么并不重要。

我想向 GET 网址发送请求,而不是被重定向。

怎么样?服务器在您的GET 请求中以302 响应。如果您再次在同一 URL 上执行 GET,您将再次被重定向。

你想达到什么目的?

如果您不想被重定向,请查看以下问题:

Avoiding redirection Facebook url returning an mobile version url response in scrapy How to avoid redirection of the webcrawler to the mobile edition?

【讨论】:

【参考方案6】:

我在使用HTTPCACHE_ENABLED = True 时遇到了重定向无限循环的问题。我通过设置HTTPCACHE_IGNORE_HTTP_CODES = [301,302] 设法避免了这个问题。

【讨论】:

以上是关于如何在scrapy中处理302重定向的主要内容,如果未能解决你的问题,请参考以下文章

302 重定向是如何发生的?

无法在 ajax 中处理 302 重定向,为啥? [复制]

如何使用 301 重定向而不是 302 将 HTTP 站点重定向到 HTTPS 站点

如何从 302 临时重定向代码更改为 301 永久重定向代码?

python 爬网页 遇到重定向怎么处理

处理 http 302 重定向响应