zoukankan      html  css  js  c++  java
  • Scrapy+seleninu抓取内容同时下载图片几个问题

    使用了Scrapy+Seleninm+Scrapy_redis抓取了详情页的内容和图片,贴出需要完善和出问题的代码,做部分解析和说明。

    # -*- coding: utf-8 -*-
    import time;

    from scrapy.linkextractors import LinkExtractor
    from scrapy.spiders import Rule
    from scrapy_redis.spiders import RedisCrawlSpider #导入RedisCrawSpider,以便使用Scrapy_redis实现1任务队列持久化(可支持暂停或重启爬虫)和2进行重复过滤
    from selenium import webdriver;#导入seleninum的Webdriver,以调用无界流利器,实现动态内容的的获取,本文用的是Chrome,当然可以用非常多的浏览器,可以看看源码,支持很多的
    from selenium.webdriver.chrome.options import Options #调用Chorme浏览器的启动参数选项

    from scrapyYF.items import ScrapyyfItem


    class YaofangSpider(RedisCrawlSpider):
    name = 'yaofang'
    allowed_domains = ['www.jian.com']
    start_urls = ['https://www.jian.com/']
    redis_key = 'JK:YP'
    # 必须是列表
    rules = [
    # follow=False(不跟进), 只提取首页符合规则的url,然后爬取这些url页面数据,callback解析
    # Follow=True(跟进链接), 在次级url页面中继续寻找符合规则的url,如此循环,直到把全站爬取完毕
    # Rule(LinkExtractor(allow=(r'/c/category?cat_id=d*$')),follow=True),#要想抓取更多,可以同时放开此项,从分类页也可以找相应的内容
    Rule(LinkExtractor(allow=(r'/product/d*.html$'), unique=True), callback='parse_druginfo', follow=True)
    # Rule(LinkExtractor(allow=(r'/product/11929.html'), unique=True), callback='parse_druginfo', follow=False)
    # Rule(LinkExtractor(allow=(r'/article/d*$'),unique=True), callback='parse_item', follow=True)
    ]

    def __init__(self, *args, **kwargs):
    super(YaofangSpider, self).__init__(*args, **kwargs)#如果不调用父类的这个,会出现AttributeError: 'xxxSpider' object has no attribute '_rules'的错误
    chrome_opt = Options(); # 创建参数设置对象.
    chrome_opt.add_argument('--headless'); # 无界面化.
    chrome_opt.add_argument('--disable-gpu'); # 配合上面的无界面化.
    chrome_opt.add_argument('--disable-infobars'); # 配合上面的无界面化.
    chrome_opt.add_argument('--window-size=1366,768'); # 设置窗口大小, 窗口大小会有影响.
    chrome_opt.add_argument('blink-settings=imagesEnabled=false'); # 禁止加载图片
    # self.bro=webdriver.Chrome(executable_path=r'D:Python27Scriptschromedriver.exe')
    self.bro = webdriver.Chrome(chrome_options=chrome_opt);

    def parse_druginfo(self, response):
    item = ScrapyyfItem();
    item['from_url'] = response.url;
    item['addtime'] = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime());
    item['updated'] = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime());
    item['class_name'] = response.xpath(
    'normalize-space(//div[@class="crumb"]/div/a[2]/text())').extract_first() + '_' + response.xpath(
    'normalize-space(//div[@class="crumb"]/div/a[3]/text())').extract_first(); # 分类,比如:中西药品_男科药品 extract_first是提取第一个内容,python3以上已经用get()来代替了getall=extract,get=extract_first
    item['goods_id'] = "jianke_" + response.xpath(
    'normalize-space(//dl[@class="assort"][1]/dd/text())').extract_first(); # 来源+唯一标识,比如:jianke_B13003000675
    item['drug_name'] = response.xpath(
    'normalize-space(//dl[@class="assort tongyong"]/dd/a/text())').extract_first(); # 药品名称:六味地黄丸
    item['goods_name'] = response.xpath(
    'normalize-space(//div[@class="det_title"]//h1/text())').extract_first(); # 商品名称:同仁堂 六味地黄丸(浓缩丸) 120s
    item['grant_number'] = response.xpath(
    'normalize-space(//dl[@class="assort"][2]/dd/span/text())').extract_first(); # '批准文号'
    item['ingredient'] = response.xpath(
    u"normalize-space(//*[@id='b_1_1']/table//tr[contains(td,'主要原料')]/td[2]/text())").extract_first(); # 主要成份:熟地黄、酒萸肉、牡丹皮、山药、茯苓、泽泻。xpath提取标题为"主要原料"后的内容
    item['indiction'] = response.xpath(
    u"normalize-space(//*[@id='b_1_1']/table//tr[contains(td,'主要作用')]/td[2]/text())").extract_first(); # 主要作用:滋阴补肾。用于肾阴亏损,头晕耳鸣,腰膝酸软,骨蒸潮热,盗汗遗精。
    item['standard'] = response.xpath(
    u'normalize-space(//*[@id="b_1_1"]/table//tr[td="产品规格"]/td[2]/text())').extract_first(); # 产品规格:120丸(浓缩丸)
    item['usages'] = response.xpath(
    u'normalize-space(//*[@id="b_1_1"]/table//tr[td="用法用量"]/td[2]/text())').extract_first(); # 用法用量:口服。一次8丸,一日3次。
    item['manual'] = "".join(response.xpath(u'//div[@id="b_2_2"]/div/child::p').extract()); # 抓取说明书的内容
    item['imgsrc'] = response.xpath(u'//div[@id="tb21"]/div//child::img/@src').extract();
    item['manufacturer'] = response.xpath(
    u'normalize-space(//*[@id="b_1_1"]/table//tr[td="生产企业"]/td[2]/text())').extract_first(); # 生产企业:北京同仁堂科技发展股份有限公司制药厂
    yield item;


    def __del__(self):
    self.bro.quit();

      代码总结:

    •  使用RedisCrawSpider,要导入Scrapy_redis可以实现1任务队列持久化(可支持暂停或重启爬虫)和2进行重复过滤
    •    使用CrawlSpider或RedisCrawSpider,如果重构_init函数时,一定要调用父类函数,不然会报AttributeError: 'xxxSpider' object has no attribute '_rules'的错误
    •   使用selenium调用webdriver以使用无界浏览器时,可以调好多无界浏览器,比如:chrome,firefox,safari等,并能设置相应的启动参数.具体,可以看https://www.cnblogs.com/jessicor/p/12072255.html
    •  extract_first是提取第一个内容,python3以上已经用get()来代替了getall=extract,get=extract_first
    •     xpath提取标题为"主要原料"后的内容//*[@id='b_1_1']/table//tr[contains(td,'主要原料')]/td[2]/text()
    class safetyChainMiddleware(object):
    def process_request(self, request, spider):
    request.headers['User_Agent'] = user_agent.generate_user_agent()#调用user_agent生成随机headers头,防止被封
    referer = request.url
    if referer:
    request.headers['Referer'] = referer#生成头信息Referer,以防止被封

    class seleniumMiddleware(object):
    def isFindElement(self, spider):
    try:
    # spider.bro.find_element_by_id('b_2').click();
    if WebDriverWait(spider.bro, 3).until(
    EC.text_to_be_present_in_element((By.XPATH, "//ul/li[@id='b_2']"), u'说明书')):#每0.05ms请求一次,请求3s钟,判断id=b_2,标题等于说明书的元素是否存在,存在则执行下边的模拟单击动作
    spider.bro.find_element_by_xpath(u"//ul/li[@id='b_2' and contains(text(),'说明书')]").click();
    pass
    except:
    # spider.bro.quit()
    pass
    def process_response(self, request, response, spider):
    url = request.url
    if string.find(url, '.jpg') != -1:#如果是图片,直接返回response,不能用HtmlResponse,不然图片不能正常返回或下载
    return response
    else:
    spider.bro.get(request.url)

    return HtmlResponse(url=spider.bro.current_url, body=page_text, encoding='utf8', request=request)#返回无界浏览器返回的response内容信息.

    • 添加自定义中间件,为了防止被封,一般要添加的是user_agent,proxy,referer和cookie,要注意,一定要在process_requess里处理,另外还要注意这个函数的返回值.不同的返回值,会执行不同操作:

        

    当每个request通过下载中间件时,该方法被调用。

    process_request() 必须返回其中之一: 返回 None 、返回一个 Response 对象、返回一个 Request 对象或raise IgnoreRequest 。

    如果其返回 None ,Scrapy将继续处理该request,执行其他的中间件的相应方法,直到合适的下载器处理函数(download handler)被调用, 该request被执行(其response被下载)。

    如果其返回 Response 对象,Scrapy将不会调用 任何 其他的 process_request() 或 process_exception() 方法,或相应地下载函数; 其将返回该response。 已安装的中间件的 process_response() 方法则会在每个response返回时被调用。

    如果其返回 Request 对象,Scrapy则停止调用 process_request方法并重新调度返回的request。当新返回的request被执行后, 相应地中间件链将会根据下载的response被调用。

    如果其raise一个 IgnoreRequest 异常,则安装的下载中间件的 process_exception() 方法会被调用。如果没有任何一个方法处理该异常, 则request的errback(Request.errback)方法会被调用。如果没有代码处理抛出的异常, 则该异常被忽略且不记录(不同于其他异常那样)。

    • process_response,这个处理response,针对图片,一定要单独返回.不要用htmlResponse.这个是无界浏览器返回的内容.我在这儿是做了判断.不然图片不能下载.纠结了好几天,最后好好看了看文档,Step by step若干遍程序,终于找到了原因.scrapy的执行流程很重要.
      process_response的返回值也需要说明下
          

    process_request() 必须返回以下之一: 返回一个 Response 对象、 返回一个 Request 对象或raise一个 IgnoreRequest 异常。

    如果其返回一个 Response (可以与传入的response相同,也可以是全新的对象), 该response会被在链中的其他中间件的 process_response() 方法处理。

    如果其返回一个 Request 对象,则中间件链停止, 返回的request会被重新调度下载。处理类似于 process_request() 返回request所做的那样。

    如果其抛出一个 IgnoreRequest 异常,则调用request的errback(Request.errback)。 如果没有代码处理抛出的异常,则该异常被忽略且不记录(不同于其他异常那样)。

     
  • 相关阅读:
    Microsoft 机器学习产品体系对比和介绍
    使用ANNdotNET进行情感分析
    使用.NET Hardware Intrinsics API加速机器学习场景
    关于ML.NET v0.6的发布说明
    强化学习的十大原则
    关于ML.NET v0.5的发布说明
    使用ML.NET实现基于RFM模型的客户价值分析
    使用ML.NET实现NBA得分预测
    Azure认知服务之Face API上手体验
    Orange——开源机器学习交互式数据分析工具
  • 原文地址:https://www.cnblogs.com/jessicor/p/12109089.html
Copyright © 2011-2022 走看看