Scrapy进阶开发

selenium模拟登录微博, 模拟鼠标下拉

# -*- coding: utf-8 -*-

from selenium import webdriver
from scrapy.selector import Selector


#知乎的模拟登录
browser = webdriver.Chrome(executable_path="E:/chromedriver.exe")  #路径是chromedriver.exe的存放的位置
browser.get("https://www.zhihu.com/#signin")
browser.find_element_by_css_selector(".view-signin input[name='account']").send_keys("********") #帐号
browser.find_element_by_css_selector(".view-signin input[name='password']").send_keys("********") #密码
browser.find_element_by_id("captcha").send_keys(input('请输入验证码:'))
browser.find_element_by_css_selector(".view-signin button.sign-button").click() #登录
browser.quit()


#可以用selenium得到js加载后的html,比如这样的话可以抓取到本来抓取的不到的一些字段(淘宝的交易量等等)
browser = webdriver.Chrome(executable_path="E:/chromedriver.exe")
browser.get("https://detail.tmall.com/item.htm?spm=a230r.1.14.3.yYBVG6&id=538286972599&cm_id=140105335569ed55e27b&abbucket=15&sku_properties=10004:709990523;5919063:6536025")
print(browser.page_source) #page_source就是js加载完的源代码
#browser.quit()
'''
如果是用selenium本身的选择器(python写的,比较慢),会很慢
所以现在转换成scrapy中的selector(他是用c语言写的,很快)
模版,也可以嵌入scrapy中
'''
t_selector=Selector(text=browser.page_source)
print(t_selector.xpath('//*[@id="J_StrPriceModBox"]/dd/span/text()').extract())

有时候取不到内容,看看是不是还没有加载完,在中间sleep几秒就好了。

import time
time.sleep(15)

模拟鼠标下拉

#selenium 完成微博模拟登录
browser = webdriver.Chrome(executable_path="E:/chromedriver.exe")
browser.get("http://weibo.com/")
import time
time.sleep(5)
browser.find_element_by_css_selector("#loginname").send_keys("******")
browser.find_element_by_css_selector(".info_list.password input[node-type='password']").send_keys("******")
browser.find_element_by_css_selector(".info_list.login_btn a[node-type='submitBtn']").click()
#下拉
for i in range(3):
    '''三次下拉操作,这是javascript的知识'''
    browser.execute_script("window.scrollTo(0, document.body.scrollHeight); var lenOfPage=document.body.scrollHeight; return lenOfPage;")
    time.sleep(3)

chromedriver不加载图片、phantomjs获取动态网页

chromedriver不加载图片

#设置chromedriver不加载图片
#是固定的模版
chrome_opt = webdriver.ChromeOptions()
prefs = {"profile.managed_default_content_settings.images":2}
chrome_opt.add_experimental_option("prefs", prefs)
browser = webdriver.Chrome(executable_path="E:/chromedriver.exe",chrome_options=chrome_opt)
browser.get("http://weibo.com/")

phantomjs获取动态网页

#phantomjs, 无界面的浏览器, 多进程情况下phantomjs性能会下降很严重
browser = webdriver.PhantomJS(executable_path="F:/迅雷下载/phantomjs-2.1.1-windows/bin/phantomjs.exe")
browser.get("https://detail.tmall.com/item.htm?spm=a230r.1.14.3.yYBVG6&id=538286972599&cm_id=140105335569ed55e27b&abbucket=15&sku_properties=10004:709990523;5919063:6536025")
print (browser.page_source)
browser.quit()

selenium集成到scrapy中

在middlewares.py中定义的一个class

from selenium import webdriver
from scrapy.http import HtmlResponse
class JSPageMiddleware(object):

    #通过chrome请求动态网页
    def process_request(self, request, spider):
        if spider.name == "jobbole":
            # browser = webdriver.Chrome(executable_path="D:/Temp/chromedriver.exe")
            spider.browser.get(request.url)
            import time
            time.sleep(3)
            print ("访问:{0}".format(request.url))

            return HtmlResponse(url=spider.browser.current_url, body=spider.browser.page_source, encoding="utf-8", request=request)

Spider中的代码

name = "JobBole"
   allowed_domains = ["jobbole.com"]
   start_urls = ['http://blog.jobbole.com/all-posts/']

   def __init__(self):
       '''chrome放在spider中,防止每打开一个url就跳出一个chrome'''
       self.browser=webdriver.Chrome(executable_path='E:/chromedriver.exe')
       self.browser.set_page_load_timeout(30)
       super(JobboleSpider, self).__init__()
       dispatcher.connect(self.spider_close,signals.spider_closed)

   def spider_close(self,spider):
       #当爬虫退出的时候关闭Chrome
       print("spider closed")
       self.browser.quit()

设置settings.py里面的文件才生效:

DOWNLOADER_MIDDLEWARES = {
'ArticleSpider.middlewares.JSPageMiddleware':543,
}

其余动态网页获取技术介绍-chrome无界面运行、scrapy-splash、selenium-grid、splinter

chrome无界面运行

from pyvirtualdisplay import Display
display = Display(visible=0, size=(800, 600))
display.start()

browser = webdriver.Chrome()
browser.get()

scrapy-splash

Splash是一个Javascript渲染服务。它是一个实现了HTTP API的轻量级浏览器,Splash是用Python实现的,同时使用Twisted和QT。Twisted(QT)用来让服务具有异步处理能力,以发挥webkit的并发能力。

可以在scrapy中处理ajax来抓取动态的数据,没有chrome那么稳定。

scrapy的暂停与重启

Pycharm中无法给Scrapy发送中断信号(Ctrl+c),因此,如果要暂停和重启Scrapy,需要使用Scrapy自带的重启机制。

在命令行下输入: scrapy crawl spider_name -s JOBDIR=spider_info_path 其中,-s参数表示个将Spider的状态信息保存到spider_info_path的路径下(对于每个Spider,该路径都要独立),然后Scrapy就能够实现暂停与重启。此外,该参数的配置还可以放在settings中实现。

custom_settings = {
   "JOBDIR" : "job_info/001"
}

如:scrapy crawl lagou -s JOBDIR=job_info/001

更多参考:https://doc.scrapy.org/en/latest/topics/jobs.html

如果要保存在不同的文件那就修改不同路径就好了,spider会重新从第一个url开始爬取。

scrapy url去重原理

  • spider启动时,RFPDupeFilter类实例化时生成一个空集合self.fingerprints = set()
  • 每发起一个请求时,RFPDupeFilterrequest_seen方法会调用request_fingerprint方法为url生成一个唯一的指纹;
  • 查看该指纹是否在集合fingerprints中,如果在,则过滤掉,不在,则对该指纹对应的url发起请求,并把指纹添加到集合fingerprints中。

scrapy telnet服务

telnet是一个运行在Scrapy进程中的普通python终端,通过我们可以查看scrapy的相关信息 命令:telnet 127.0.0.1 6023

可以在settings中设置scrapy远程连接的用户名和密码。

spider middleware

depth.py:爬取深度的设置;

httperror.py:状态的设置,比如是不是要把404的也抓取下来,等等。

scrapy信号详解

Scrapy使用信号来通知事情发生。您可以在您的Scrapy项目中捕捉一些信号(使用 extension)来完成额外的工作或添加额外的功能,扩展Scrapy。

#收集伯乐在线所有404的url以及404页面数
    handle_httpstatus_list = [404]

    def __init__(self, **kwargs):
        self.fail_urls = []
        dispatcher.connect(self.handle_spider_closed, signals.spider_closed)

    def handle_spider_closed(self, spider, reason):
        self.crawler.stats.set_value("failed_urls", ",".join(self.fail_urls))

    def parse(self, response):
        """
        1. 获取文章列表页中的文章url并交给scrapy下载后并进行解析
        2. 获取下一页的url并交给scrapy进行下载, 下载完成后交给parse
        """
        #解析列表页中的所有文章url并交给scrapy下载后并进行解析
        if response.status == 404:
            self.fail_urls.append(response.url)
            self.crawler.stats.inc_value("failed_url")

        post_nodes = response.css("#archive .floated-thumb .post-thumb a")
        for post_node in post_nodes:
            image_url = post_node.css("img::attr(src)").extract_first("")
            post_url = post_node.css("::attr(href)").extract_first("")
            yield Request(url=parse.urljoin(response.url, post_url), meta={"front_image_url":image_url}, callback=self.parse_detail)

本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!