使用Scrapy根据DOI下载文献到本地

使用Scrapy根据DOI下载文献到本地

Scrapy框架专门提供了用于文件下载的FilesPipline和用于图片下载的ImagePipline

爬取策略

  1. 从Mysql数据库中提取DOI号
  2. 拼接url:‘http://www.sci-hub.ren/’+doi 指向doi对应的pdf页面
  3. 在该页面中通过xpath和正则表达式,将指向pdf的url提取出来,交付给pipline进行下载

具体步骤

  1. 设置setting.py
    # setting.py
    # 框架自动生成无需修改
    BOT_NAME = 'DoiDown'
    
    SPIDER_MODULES = ['DoiDown.spiders']
    NEWSPIDER_MODULE = 'DoiDown.spiders'
    # 设置USER_AGENT
    USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36"
    # 不遵从robots协议
    ROBOTSTXT_OBEY = False
    # 设置日志等级
    LOG_LEVEL = 'ERROR'
    # 将日志保存到日志文件
    LOG_FILE = './log.log'
    # 设置下载延迟
    DOWNLOAD_DELAY = 3
    # 开启管道并设置多管道优先级
    ITEM_PIPELINES = {
         
        'DoiDown.pipelines.initPipeline': 200,
        'DoiDown.pipelines.fileDown': 300
    }
    # 设定FilePipline的文件保存路径
    FILES_STORE = './PDF'
    
  2. 定义item
     # items.py
     import scrapy
    
     class DoidownItem(scrapy.Item):
         # define the fields for your item here like:
         # name = scrapy.Field()
         file_urls = scrapy.Field()
         files = scrapy.Field()
         pass
    
    file_urlsfile都是item的字段。FilesPipline要求file_urls必须是一个列表,存的是待下载文件的url。文件下载完成之后,file包含了文件的路径、文件校验、文件的url等信息。
  3. 编写spider
     # doi_down.py
    
     import pymysql
     import scrapy
     from scrapy import Request
     import re
     from ..items import DoidownItem
    
    
     class DoiDownSpider(scrapy.Spider):
         name = 'doi_down'
         # allowed_domains = ['sci-hub.ren']
         connect = None
         cursor = None
    
         # start_urls = ['http://sci-hub.ren/']
    
         def start_requests(self):
             # 自定义开始爬取的url。spider从这个函数开始执行。
             self.connect = pymysql.connect(
                 host="localhost",
                 port=3306,
                 user="root",
                 passwd="xxxxxx",
                 db="web_of_science")
             self.cursor = self.connect.cursor()
             self.cursor.execute("select doi from wos_document;")
             doi_lists = list(self.cursor.fetchall())
    
             for doi in doi_lists[:50]:
                 if doi[0] is None:
                     print('该DOI为空...')
                 else:
                     detail_url = 'https://www.sci-hub.ren/' + doi[0]
                     print(detail_url)
                     # 发起url请求,并指定处理响应的回调函数
                     yield Request(detail_url, callback=self.parse)
    
             self.connect.close()
    
         def parse(self, response):
             # 该函数用于解析响应数据
             print('解析中!!!')
             detail_url_list = response.xpath('//*[@id="buttons"]/ul/li[2]/a/@onclick')
             item = DoidownItem()
             item['file_urls'] = []
             if len(detail_url_list) == 0:
                 print('*' * 10 + '已自动跳转第三方网站,不予处理!!!' + '*' * 10)
             else:
                 # 提取第一个目标Selector的内容
                 target = detail_url_list.extract_first()
                 find_url = re.compile(r"href='(.*?)'")
                 target_url = re.findall(find_url, target)[0]
                 if target_url[0] != 'h':
                     target_url = 'https:' + target_url
                 item['file_urls'].append(target_url)
                 # 这里解析到了pdf文件对应的url;yield的作用是交付给pipline.py处理
                 yield item
    
    
  4. 自定义pipelines
     
     import re
    
     # useful for handling different item types with a single interface
     from scrapy import Request
     from scrapy.pipelines.files import FilesPipeline
    
    
     class initPipeline:
         def open_spider(self, spider):
             print('*' * 10 + '开始下载...' + '*' * 10)
    
         def process_item(self, item, spider):
             return item
    
         def close_spider(self, spider):
             print('*' * 10 + '爬取完毕' + '*' * 10)
    
    
     class fileDown(FilesPipeline):
         def get_media_requests(self, item, info):
             # 向FilesPipline提交url地址,进行文件下载
             for url in item['file_urls']:
                 yield Request(url)
    
         def file_path(self, request, response=None, info=None):
             # 设置保存的文件名
             find_name = re.compile(r"/(.*?)?download=true")
             file_name = re.findall(find_name, request.url)[0]
             file_name = file_name.split('/')[-1]
             file_name = file_name[0:len(file_name) - 1]
             print(file_name, '正在下载!!!')
             return file_name
    
    之前已经在settings.py中设定了initPipline优先级较高。在initPipline中自定义了一个管道,爬虫程序从open_spider开始执行。然后执行spider,也就是doi_down.py。程序执行完close_spider后 结束。
    使用FilesPipeline下载文件,需要在管道中从scrapy.pipelines.files import进来。然后作为参数传入自定义的用于下载文件的管道类中。fileDownget_media_requests是用于发起下载请求的函数;file_path是用于设定保存文件名的函数。这两个函数的函数名是固定的,也就是说需要用FilesPipeline自定义的管道,要自定义请求的发起和保存的文件名的设定,对应的函数名必须这样命名。
全部评论

相关推荐

评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务