V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
wqzjk393
V2EX  ›  Python

问一个关于 scrapy 爬虫遇到反扒的问题

  •  
  •   wqzjk393 · 2019-11-22 00:19:26 +08:00 · 4172 次点击
    这是一个创建于 1847 天前的主题,其中的信息可能已经有所发展或是发生改变。

    爬蚂蜂窝,然后遇到了 521response 反爬。自己搜了一下这个反爬还好处理,就是解码设置 cookie 的事。 但是我后来测试时候发现其实只要设置一个 5 秒左右的延时,通过 selenium 返回的 page_source 就是跳过反爬信息得到的完整页面信息了。

    于是综上探索,我预想的是在 scrapy 中间件中加入 selenium,在 process_request 里做 get 请求之后,time.sleep 四五秒钟,然后返回 response 做后续处理。

    想的是很美好,但是测试时候发现,因为中间件的 selenium 做 get 请求时,url 地址是 request.url,而这个 url 好像因为之前 scrapy 自带的 request 没有通过反爬然后就从一开始 url 就变成了 www.mafengwo.cn/robot.txt ,那这个 url 肯定是没法用了。用 spider.start_urls 的话又要处理一个列表问题,如果是爬好几个页面感觉从这个列表提取 url 也不是很靠谱啊。

    那么有什么靠谱点的办法,让 request.url 传递过去正常的 url 呢?

    附上 selenium 中间件的代码:

    class SeleniumMiddleWares(object):
    
        def __init__(self):
            self.driver_path = '/Users/michealki/Downloads/chromedriver'
            self.options = Options()
            self.options.add_experimental_option('excludeSwitches', ['enable-automation'])
            # self.options.add_argument('--headless')
            self.driver = selenium.webdriver.Chrome(executable_path=self.driver_path,options=self.options)
    
        @classmethod
        def from_crawler(cls, crawler):
            s = cls()
            crawler.signals.connect(s.spider_closed, signal=signals.spider_closed)
            return s
    
        def process_request(self, request, spider):
            #url =  request.url
            url = spider.start_urls[0]
            self.driver.get(url)
            time.sleep(5)
            return HtmlResponse(url=self.driver.current_url, body=self.driver.page_source,
                                encoding="utf-8", request=request)
    
        def spider_closed(self, spider):
            self.driver.quit()
    
    		
    class Spider0(scrapy.Spider):
            name = 'spider0'
            start_urls = ['http://www.mafengwo.cn/poi/7918553.html']
    
            def parse(self,response):
                    pass
    
    
    
    16 条回复    2019-11-22 17:03:53 +08:00
    wqzjk393
        1
    wqzjk393  
    OP
       2019-11-22 00:21:31 +08:00
    另外求教一下。。。md 下面怎么正常换行呢?直接回车的话实际显示出来总是发现回车没有效果,中间空一行的话又感觉格式好丑
    locoz
        2
    locoz  
       2019-11-22 00:27:52 +08:00
    @wqzjk393 #1 用 typora 之类的 markdown 编辑器,或者在每一行后面加两个空格再换行
    locoz
        3
    locoz  
       2019-11-22 00:31:07 +08:00
    “而这个 url 好像因为之前 scrapy 自带的 request 没有通过反爬然后就从一开始 url 就变成 www.mafengwo.cn/robot.txt”这个描述,看起来应该是你没关闭 scrapy 的 robots 文件检测导致的?
    locoz
        4
    locoz  
       2019-11-22 00:31:51 +08:00
    “用 spider.start_urls 的话又要处理一个列表问题,如果是爬好几个页面感觉从这个列表提取 url 也不是很靠谱啊。”
    看不出哪里不靠谱,什么叫“又要处理一个列表问题”?
    dreamerlv3ex
        5
    dreamerlv3ex  
       2019-11-22 09:16:38 +08:00
    scrapy 担心你进去,帮你画了一条线,看你要不要越过吧..你要越过去就去设置配置就行..
    wqzjk393
        6
    wqzjk393  
    OP
       2019-11-22 09:20:46 +08:00
    @locoz
    @dreamerlv3ex
    原来可以直接越过 robot 检测啊。。。我还以为这个是网站检测到爬虫程序然后强制把 response 从页面跳到了 robot 这里,那看起来应该是 scrapy 主动去 robot 里检测是否在人家准许爬取的范围内了。嗯 robot 这方面了解的还是太少了,多谢指教~
    wqzjk393
        7
    wqzjk393  
    OP
       2019-11-22 09:24:44 +08:00
    @locoz 简单说就是,start_urls 是一个列表,如果我引入中间件并在中间件通过 start_urls 访问里面的某个 url,需要一个 index 来索引这个列表啊,这个 index 怎么传过去呢?如果就是一个页面那我直接 start_urls[0]就行,但是多个页面的话我不知道有没有哪个参数能表明当前爬取的 start_urls 是第几个
    skinny
        8
    skinny  
       2019-11-22 10:18:12 +08:00
    settings.py 里关闭 ROBOT.TXT 检测,更换 UA ;并不是强制必须从 start_urls 开始,可以重写 start_requests 方法,yield 请求,parse callback 里也可以 yield 请求。

    话说我觉得 scrapy 太难用了,需求多一点就要改很多东西,精确数据爬取还是自己写的灵活。
    locoz
        9
    locoz  
       2019-11-22 10:57:19 +08:00
    @wqzjk393 #6 一看你就是没看文档...我记得文档上写得清清楚楚地说会检测 URL 是否被 robots.txt 所禁止 balabala。

    @wqzjk393 #7 虽然我没用过 Scrapy,但是从我这么长时间看到的用法来看,使用 Scrapy 的时候被访问的 URL 并不需要你去从 start_urls 里取,框架已经帮你处理好取 URL 这一步了,你要做的只是重写回调而已,而且在这个流程中你也不需要管当前爬的是第几个。
    如果你一定要知道当前爬的是第几个、要可控性非常高的话,我建议你直接自己重新造轮子。基于一个成熟且不是自己写的框架去改的话大概率会碰到奇奇怪怪的问题。
    GPU
        10
    GPU  
       2019-11-22 11:25:37 +08:00
    scrapy-splash 这个东西了解一下?

    之前我搞爬虫的时候也是找到 selenium, 但是配置太麻烦最后用了前者很简单。
    这个东西好似好似是 scrapy 开源出来的
    wqzjk393
        11
    wqzjk393  
    OP
       2019-11-22 12:01:16 +08:00
    @locoz 嗯。。试了一下,scrapy 会先产生一个 robot 的 request 然后再产生一个真正 url 的 request。就是因为首先产生了 robot 请求,然后我中间件没有判断 url 是不是 xxx/robot.txt 就直接拿去做自定义 request_process 了,那第二次真正的 url 请求自然就不会传到中间件了。用 request.url 是没问题的。
    wqzjk393
        12
    wqzjk393  
    OP
       2019-11-22 12:07:09 +08:00
    @GPU selenium 其实我也只是需要它动态加载和手动执行 js 脚本的功能,其他的 xpath 啊 selector 之类的有很多可以替代的东西。scrapy-splash 好像也可以做动态加载,改天试一下~

    @skinny 嗯,robot 和 start_urls、request 那些差不多弄明白了。其实我自己写爬虫的话大部分时候都是 request+xpath。只是作为一个学爬虫的 scrapy 都不会用感觉有点说不过去。而且异步这方面 aiohttp 这些真的是不想自己研究了。。。
    GPU
        13
    GPU  
       2019-11-22 12:13:13 +08:00
    @wqzjk393 #12 scrpay-splash 完全可以替代 selenium,xpath 之类的功能是 scrapy 自带的把
    warcraft1236
        14
    warcraft1236  
       2019-11-22 13:43:25 +08:00
    @GPU splash 渲染出来的页面和浏览器渲染出来的还是有差别。之前有个网站把视频的地址换成了加密字符串,需要执行一段 js 来解密,我发现用 splash 并不能正常的解密出来地址
    RicardoY
        15
    RicardoY  
       2019-11-22 13:59:13 +08:00 via Android
    你不应该在 middleware 里直接处理 start_urls 这个列表 ..直接处理 request 不就好了吗
    wqzjk393
        16
    wqzjk393  
    OP
       2019-11-22 17:03:53 +08:00
    @RicardoY 对的,后来自己有研究了一下就是应该处理 request 的。我之前是不知道 scrapy 会首先请求 domain 下的 robot 做检测,然后第二次才会发送真正 url 的请求。所以在我中间件处理时候没有检测 url 是不是 domain+robot.txt 直接提取出来 request.url 丢给 selenium 了,所以就出错了,问题不在于 request.url 这个东西,而是我没有想到 robot 的问题
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   851 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 21:33 · PVG 05:33 · LAX 13:33 · JFK 16:33
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.