Gerapy / GerapyPyppeteer

Downloader Middleware to support Pyppeteer in Scrapy & Gerapy
137 stars 37 forks source link

Pyppeteer setRequestInterception(True) 在某些网站下中断网页 #6

Closed Deemonser closed 4 years ago

Deemonser commented 4 years ago

在拉勾教育上学习的,所有直接用拉勾网址测试: https://kaiwu.lagou.com/course/courseInfo.htm?courseId=46#/detail/pc?id=1661 用 Scrapy 框架,配合本库,网页仅加载了一部分。 经过多次测试,发现 setRequestInterception(True) 这个设置导致的。 Google 后发现,这个Bug 应该是 Pyppeteer 的问题。 希望这个设置可以由外部开启关闭

Germey commented 4 years ago

请问方便发下项目或者报错信息吗?

Deemonser commented 4 years ago

只是学习用的练习项目,非常简单

class LagouSpider(scrapy.Spider):
    name = 'lagou'
    allowed_domains = ['kaiwu.lagou.com']
    start_urls = ['https://kaiwu.lagou.com/course/courseInfo.htm?courseId=185#/detail/pc?id=3329']

    def start_requests(self):
        from gerapy_pyppeteer import PyppeteerRequest
        yield PyppeteerRequest(self.start_urls[0], callback=self.parse, wait_until='networkidle0')

    def parse(self, response):
        save_html(response.body.decode(), f'test-{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}.html')

在设置中 GERAPY_PYPPETEER_HEADLESS = False 观察浏览器,发现并没有加载完全。 控制台上没有任何错误信息,只是正常打印信息。

2020-07-27 14:05:21 [scrapy.utils.log] INFO: Scrapy 2.2.1 started (bot: lagou_edu_spider)
2020-07-27 14:05:21 [scrapy.utils.log] INFO: Versions: lxml 4.5.0.0, libxml2 2.9.10, cssselect 1.1.0, parsel 1.6.0, w3lib 1.22.0, Twisted 20.3.0, Python 3.7.4 (default, Sep 24 2019, 15:03:50) - [Clang 10.0.0 (clang-1000.11.45.5)], pyOpenSSL 19.1.0 (OpenSSL 1.1.1g  21 Apr 2020), cryptography 2.9.2, Platform Darwin-17.7.0-x86_64-i386-64bit
2020-07-27 14:05:21 [scrapy.utils.log] DEBUG: Using reactor: twisted.internet.selectreactor.SelectReactor
2020-07-27 14:05:21 [scrapy.crawler] INFO: Overridden settings:
{'BOT_NAME': 'lagou_edu_spider',
 'FEED_EXPORT_ENCODING': 'utf-8',
 'NEWSPIDER_MODULE': 'lagou_edu_spider.spiders',
 'SPIDER_MODULES': ['lagou_edu_spider.spiders']}
2020-07-27 14:05:21 [scrapy.extensions.telnet] INFO: Telnet Password: dd1dfefa1a961acf
2020-07-27 14:05:21 [scrapy.middleware] INFO: Enabled extensions:
['scrapy.extensions.corestats.CoreStats',
 'scrapy.extensions.telnet.TelnetConsole',
 'scrapy.extensions.memusage.MemoryUsage',
 'scrapy.extensions.feedexport.FeedExporter',
 'scrapy.extensions.logstats.LogStats']
2020-07-27 14:05:21 [asyncio] DEBUG: Using selector: KqueueSelector
2020-07-27 14:05:21 [scrapy.middleware] INFO: Enabled downloader middlewares:
['scrapy.downloadermiddlewares.httpauth.HttpAuthMiddleware',
 'scrapy.downloadermiddlewares.downloadtimeout.DownloadTimeoutMiddleware',
 'scrapy.downloadermiddlewares.defaultheaders.DefaultHeadersMiddleware',
 'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware',
 'gerapy_pyppeteer.downloadermiddlewares.PyppeteerMiddleware',
 'scrapy.downloadermiddlewares.retry.RetryMiddleware',
 'scrapy.downloadermiddlewares.redirect.MetaRefreshMiddleware',
 'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware',
 'scrapy.downloadermiddlewares.redirect.RedirectMiddleware',
 'scrapy.downloadermiddlewares.cookies.CookiesMiddleware',
 'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware',
 'scrapy.downloadermiddlewares.stats.DownloaderStats']
2020-07-27 14:05:21 [scrapy.middleware] INFO: Enabled spider middlewares:
['scrapy.spidermiddlewares.httperror.HttpErrorMiddleware',
 'scrapy.spidermiddlewares.offsite.OffsiteMiddleware',
 'scrapy.spidermiddlewares.referer.RefererMiddleware',
 'scrapy.spidermiddlewares.urllength.UrlLengthMiddleware',
 'scrapy.spidermiddlewares.depth.DepthMiddleware']
2020-07-27 14:05:21 [scrapy.middleware] INFO: Enabled item pipelines:
[]
2020-07-27 14:05:21 [scrapy.core.engine] INFO: Spider opened
2020-07-27 14:05:21 [scrapy.extensions.logstats] INFO: Crawled 0 pages (at 0 pages/min), scraped 0 items (at 0 items/min)
2020-07-27 14:05:21 [scrapy.extensions.telnet] INFO: Telnet console listening on 127.0.0.1:6023
2020-07-27 14:05:21 [gerapy.pyppeteer] DEBUG: processing request <GET https://kaiwu.lagou.com/course/courseInfo.htm?courseId=185#/detail/pc?id=3329>
2020-07-27 14:05:21 [gerapy.pyppeteer] DEBUG: pyppeteer_meta {'wait_until': 'networkidle0', 'wait_for': None, 'script': None, 'sleep': None, 'proxy': None, 'timeout': None, 'ignore_resource_types': None}
2020-07-27 14:05:21 [gerapy.pyppeteer] DEBUG: set options {'headless': False, 'dumpio': False, 'devtools': False, 'args': ['--window-size=1400,700', '--disable-extensions', '--hide-scrollbars', '--mute-audio', '--no-sandbox', '--disable-setuid-sandbox', '--disable-gpu'], 'ignoreDefaultArgs': ['--enable-automation'], 'handleSIGINT': True, 'handleSIGTERM': True, 'handleSIGHUP': True}
2020-07-27 14:05:22 [gerapy.pyppeteer] DEBUG: crawling https://kaiwu.lagou.com/course/courseInfo.htm?courseId=185#/detail/pc?id=3329
2020-07-27 14:05:22 [gerapy.pyppeteer] DEBUG: request https://kaiwu.lagou.com/course/courseInfo.htm?courseId=185#/detail/pc?id=3329 with options {'timeout': 180000, 'waitUntil': 'networkidle0'}
2020-07-27 14:05:25 [gerapy.pyppeteer] DEBUG: close pyppeteer
2020-07-27 14:05:25 [scrapy.core.engine] DEBUG: Crawled (200) <GET https://kaiwu.lagou.com/course/courseInfo.htm?courseId=185#/detail/pc?id=3329> (referer: None)
2020-07-27 14:05:25 [scrapy.core.engine] INFO: Closing spider (finished)
2020-07-27 14:05:25 [scrapy.statscollectors] INFO: Dumping Scrapy stats:
{'downloader/response_bytes': 216514,
 'downloader/response_count': 1,
 'downloader/response_status_count/200': 1,
 'elapsed_time_seconds': 3.857265,
 'finish_reason': 'finished',
 'finish_time': datetime.datetime(2020, 7, 27, 6, 5, 25, 534295),
 'log_count/DEBUG': 8,
 'log_count/INFO': 10,
 'memusage/max': 56299520,
 'memusage/startup': 56295424,
 'response_received_count': 1,
 'scheduler/dequeued': 1,
 'scheduler/dequeued/memory': 1,
 'scheduler/enqueued': 1,
 'scheduler/enqueued/memory': 1,
 'start_time': datetime.datetime(2020, 7, 27, 6, 5, 21, 677030)}

但,只要把本库的本地文件 downloadermiddlewares.py中, setRequestInterception(True) 这方法 False 就能正常加载出来 看网上说是浏览器版本有关?我使用的是本地浏览器,当前版本 版本 84.0.4147.89(正式版本) (64 位)

Germey commented 4 years ago
@page.on('request')
async def _handle_interception(pu_request):
    # handle headers
    overrides = {
        'headers': {
            k.decode(): ','.join(map(lambda v: v.decode(), v))
            for k, v in request.headers.items()
        }
    }
    # handle resource types
    _ignore_resource_types = self.ignore_resource_types
    if request.meta.get('pyppeteer', {}).get('ignore_resource_types') is not None:
        _ignore_resource_types = request.meta.get('pyppeteer', {}).get('ignore_resource_types')
    if pu_request.resourceType in _ignore_resource_types:
        await pu_request.abort()
    else:
        await pu_request.continue_(overrides)

现在默认生效的就是 headers 的复写,是和这个有关系吗?

我不太确定。

然后为了方便,我把这个开关放出来了,如果要关闭,可以设置:

GERAPY_ENABLE_REQUEST_INTERCEPTION = False
xkungfu commented 3 years ago

我也遇到同样的问题了, 报错信息: 2021-02-19 11:14:04 [py.warnings] WARNING: e:\pythonwork\scrapywork\branch\env\lib\site-packages\pyee_base.py:81: RuntimeWarning: coroutine 'PyppeteerMiddleware._process_request.._handle_interception' was never awaited f(*args, **kwargs)

设置为GERAPY_ENABLE_REQUEST_INTERCEPTION = False解决了。 采集的页面是:https://dynamic5.scrape.center/page/3

我是新手,还没太看明白代码,不明白_handle_interception做了什么操作。

@Germey @Deemonser

xkungfu commented 3 years ago

用 asyncio 开发 — Python 3.9.2rc1 文档 https://docs.python.org/zh-cn/3/library/asyncio-dev.html 文档里有一段好像和这个问题有关, 但是_handle_interception内的代码里已经有了await,那就很奇怪了。。。 它的意思好像是说调用_handle_interception时,_handle_interception前面要加await?但是又没找到怎么样调用的_handle_interception。

检测 never-awaited 协同程序
当协程函数被调用而不是被等待时 (即执行 coro() 而不是 await coro()) 或者协程没有通过 asyncio.create_task() 被排入计划日程,asyncio 将会发出一条 RuntimeWarning:

import asyncio

async def test():
    print("never scheduled")

async def main():
    test()

asyncio.run(main())
输出:

test.py:7: RuntimeWarning: coroutine 'test' was never awaited
  test()
调试模式的输出:

test.py:7: RuntimeWarning: coroutine 'test' was never awaited
Coroutine created at (most recent call last)
  File "../t.py", line 9, in <module>
    asyncio.run(main(), debug=True)

  < .. >

  File "../t.py", line 7, in main
    test()
  test()
通常的修复方法是等待协程或者调用 asyncio.create_task() 函数:

async def main():
    await test()
检测就再也没异常