使用python的scrapy来编写一个爬虫

作者: 烟雨丿丶蓝 | 来源:发表于2019-04-27 21:15 被阅读7次

    本文将介绍我是如何在python爬虫里面一步一步踩坑,然后慢慢走出来的,期间碰到的所有问题我都会详细说明,让大家以后碰到这些问题时能够快速确定问题的来源,后面的代码只是贴出了核心代码,更详细的代码暂时没有贴出来。

    流程一览

    首先我是想爬某个网站上面的所有文章内容,但是由于之前没有做过爬虫(也不知道到底那个语言最方便),所以这里想到了是用python来做一个爬虫(毕竟人家的名字都带有爬虫的含义),我这边是打算先将所有从网站上爬下来的数据放到ElasticSearch里面, 选择ElasticSearch的原因是速度快,里面分词插件,倒排索引,需要数据的时候查询效率会非常好(毕竟爬的东西比较多),然后我会将所有的数据在ElasticSearch的老婆kibana里面将数据进行可视化出来,并且分析这些文章内容,可以先看一下预期可视化的效果(上图了),这个效果图是kibana6.4系统给予的帮助效果图(就是说你可以弄成这样,我也想弄成这样)。后面我会发一个dockerfile上来(现在还没弄)。

    image

    Python学习交流群:556370268,有大牛答疑,有资源共享!有想学习python编程的,想提升自己能力的,欢迎加入讨论学习。

    环境需求

    1. Jdk (Elasticsearch需要)
    2. ElasticSearch (用来存储数据)
    3. Kinaba (用来操作ElasticSearch和数据可视化)
    4. Python (编写爬虫)
    5. Redis (数据排重)

    这些东西可以去找相应的教程安装,我这里只有ElasticSearch的安装点我获取安装教程

    第一步,使用python的pip来安装需要的插件(第一个坑在这儿)

    tomd:将html转换成markdown

    pip3 install tomd
    
    

    redis:需要python的redis插件

    pip3 install redis
    
    

    scrapy:框架安装(坑)

    首先我是像上面一样执行了

    pip3 install scrapy
    
    

    然后发现缺少gcc组件 error: command 'gcc' failed with exit status 1

    image

    然后我就找啊找,找啊找,最后终于找到了正确的解决方法(期间试了很多错误答案)。最终的解决办法就是使用yum来安装python34-devel, 这个python34-devel根据你自己的python版本来,可能是python-devel,是多少版本就将中间的34改成你的版本, 我的是3.4.6

    yum install python34-devel
    
    

    安装完成过后使用命令 scrapy 来试试吧。

    image

    第二步,使用scrapy来创建你的项目

    输入命令scrapy startproject scrapyDemo, 来创建一个爬虫项目

    liaochengdeMacBook-Pro:scrapy liaocheng$ scrapy startproject scrapyDemo
    New Scrapy project 'scrapyDemo', using template directory '/usr/local/lib/python3.7/site-packages/scrapy/templates/project', created in:
        /Users/liaocheng/script/scrapy/scrapyDemo
    You can start your first spider with:
        cd scrapyDemo
        scrapy genspider example example.com
    liaochengdeMacBook-Pro:scrapy liaocheng$ 
    
    

    使用genspider来生成一个基础的spider,使用命令scrapy genspider demo juejin.im, 后面这个网址是你要爬的网站,我们先爬自己家的

    liaochengdeMacBook-Pro:scrapy liaocheng$ scrapy genspider demo juejin.im
    Created spider 'demo' using template 'basic'
    liaochengdeMacBook-Pro:scrapy liaocheng$ 
    
    

    查看生成的目录结构

    image

    第三步,打开项目,开始编码

    查看生成的的demo.py的内容

    # -*- coding: utf-8 -*-
    import scrapy
    class DemoSpider(scrapy.Spider):
     name = 'demo' ## 爬虫的名字
     allowed_domains = ['juejin.im'] ## 需要过滤的域名,也就是只爬这个网址下面的内容
     start_urls = ['https://juejin.im/post/5c790b4b51882545194f84f0'] ## 初始url链接
     def parse(self, response): ## 如果新建的spider必须实现这个方法
     pass
    
    

    可以使用第二种方式,将start_urls给提出来

    # -*- coding: utf-8 -*-
    import scrapy
    class DemoSpider(scrapy.Spider):
     name = 'demo' ## 爬虫的名字
     allowed_domains = ['juejin.im'] ## 需要过滤的域名,也就是只爬这个网址下面的内容
     def start_requests(self):
     start_urls = ['http://juejin.im/'] ## 初始url链接
     for url in start_urls:
     # 调用parse
     yield scrapy.Request(url=url, callback=self.parse)
     def parse(self, response): ## 如果新建的spider必须实现这个方法
     pass
    
    

    编写articleItem.py文件(item文件就类似java里面的实体类)

    import scrapy
    class ArticleItem(scrapy.Item): ## 需要实现scrapy.Item文件
     # 文章id
     id = scrapy.Field()
     # 文章标题
     title = scrapy.Field()
     # 文章内容
     content = scrapy.Field()
     # 作者
     author = scrapy.Field()
     # 发布时间
     createTime = scrapy.Field()
     # 阅读量
     readNum = scrapy.Field()
     # 点赞数
     praise = scrapy.Field()
     # 头像
     photo = scrapy.Field()
     # 评论数
     commentNum = scrapy.Field()
     # 文章链接
     link = scrapy.Field()
    
    

    编写parse方法的代码

     def parse(self, response):
     # 获取页面上所有的url
     nextPage = response.css("a::attr(href)").extract()
     # 遍历页面上所有的url链接,时间复杂度为O(n)
     for i in nextPage:
     if nextPage is not None:
     # 将链接拼起来
     url = response.urljoin(i)
     # 必须是掘金的链接才进入
     if "juejin.im" in str(url):
     # 存入redis,如果能存进去,就是一个没有爬过的链接
     if self.insertRedis(url) == True:
     # dont_filter作用是是否过滤相同url true是不过滤,false为过滤,我们这里只爬一个页面就行了,不用全站爬,全站爬对对掘金不是很友好,我么这里只是用来测试的 
     yield scrapy.Request(url=url, callback=self.parse,headers=self.headers,dont_filter=False)
     # 我们只分析文章,其他的内容都不管
     if "/post/" in response.url and "#comment" not in response.url:
     # 创建我们刚才的ArticleItem
     article = ArticleItem()
     # 文章id作为id
     article['id'] = str(response.url).split("/")[-1]
     # 标题
     article['title'] = response.css("#juejin > div.view-container > main > div > div.main-area.article-area.shadow > article > h1::text").extract_first()
     # 内容
     parameter = response.css("#juejin > div.view-container > main > div > div.main-area.article-area.shadow > article > div.article-content").extract_first()
     article['content'] = self.parseToMarkdown(parameter)
     # 作者
     article['author'] = response.css("#juejin > div.view-container > main > div > div.main-area.article-area.shadow > article > div:nth-child(6) > meta:nth-child(1)::attr(content)").extract_first()
     # 创建时间
     createTime = response.css("#juejin > div.view-container > main > div > div.main-area.article-area.shadow > article > div.author-info-block > div > div > time::text").extract_first()
     createTime = str(createTime).replace("年", "-").replace("月", "-").replace("日","")
     article['createTime'] = createTime
     # 阅读量
     article['readNum'] = int(str(response.css("#juejin > div.view-container > main > div > div.main-area.article-area.shadow > article > div.author-info-block > div > div > span::text").extract_first()).split(" ")[1])
     # 点赞数
     article['badge'] = response.css("#juejin > div.view-container > main > div > div.article-suspended-panel.article-suspended-panel > div.like-btn.panel-btn.like-adjust.with-badge::attr(badge)").extract_first()
     # 评论数
     article['commentNum'] = response.css("#juejin > div.view-container > main > div > div.article-suspended-panel.article-suspended-panel > div.comment-btn.panel-btn.comment-adjust.with-badge::attr(badge)").extract_first()
     # 文章链接
     article['link'] = response.url
     # 这个方法和很重要(坑),之前就是由于执行yield article, pipeline就一直不能获取数据
     yield article
    # 将内容转换成markdown
    def parseToMarkdown(self, param):
     return tomd.Tomd(str(param)).markdown
    # url 存入redis,如果能存那么就没有该链接,如果不能存,那么就存在该链接
    def insertRedis(self, url):
     if self.redis != None:
     return self.redis.sadd("articleUrlList", url) == 1
     else:
     self.redis = self.redisConnection.getClient()
     self.insertRedis(url)
    
    

    编写pipeline类,这个pipeline是一个管道,可以将所有yield关键字返回的数据都交给这个管道处理,但是需要在settings里面配置一下pipeline才行

    from elasticsearch import Elasticsearch
    class ArticlePipelines(object):
     # 初始化
     def __init__(self):
     # elasticsearch的index
     self.index = "article"
     # elasticsearch的type
     self.type = "type"
     # elasticsearch的ip加端口
     self.es = Elasticsearch(hosts="localhost:9200")
     # 必须实现的方法,用来处理yield返回的数据
     def process_item(self, item, spider):
    
     # 这里是判断,如果是demo这个爬虫的数据才处理
     if spider.name != "demo":
     return item
     result = self.checkDocumentExists(item)
     if result == False:
     self.createDocument(item)
     else:
     self.updateDocument(item)
     # 添加文档
     def createDocument(self, item):
     body = {
     "title": item['title'],
     "content": item['content'],
     "author": item['author'],
     "createTime": item['createTime'],
     "readNum": item['readNum'],
     "praise": item['praise'],
     "link": item['link'],
     "commentNum": item['commentNum']
     }
     try:
     self.es.create(index=self.index, doc_type=self.type, id=item["id"], body=body)
     except:
     pass
     # 更新文档
     def updateDocument(self, item):
     parm = {
     "doc" : {
     "readNum" : item['readNum'],
     "praise" : item['praise']
     }
     }
     try:
     self.es.update(index=self.index, doc_type=self.type, id=item["id"], body=parm)
     except:
     pass
     # 检查文档是否存在
     def checkDocumentExists(self, item):
     try:
     self.es.get(self.index, self.type, item["id"])
     return True
     except:
     return False
    
    

    第四步,运行代码查看效果

    使用scrapy list查看本地的所有爬虫

    liaochengdeMacBook-Pro:scrapyDemo liaocheng$ scrapy list
    demo
    liaochengdeMacBook-Pro:scrapyDemo liaocheng$ 
    
    

    使用scrapy crawl demo来运行爬虫

     scrapy crawl demo
    
    

    到kibana里面看爬到的数据,执行下面的命令可以看到数据

    GET /article/_search
    {
     "query": {
     "match_all": {}
     }
    }
    复制代码
    {
     "took": 7,
     "timed_out": false,
     "_shards": {
     "total": 5,
     "successful": 5,
     "skipped": 0,
     "failed": 0
     },
     "hits": {
     "total": 1,
     "max_score": 1,
     "hits": [
     {
     "_index": "article2",
     "_type": "type",
     "_id": "5c790b4b51882545194f84f0",
     "_score": 1,
     "_source": {}
     }
     ]
     }
    }
    

    如果你依然在编程的世界里迷茫,不知道自己的未来规划,对python感兴趣,这里推荐一下我的学习交流群:556370268,里面都是学习python的,从最基础的python【python,游戏,黑客技术,网络安全,数据挖掘,爬虫】到网络安全的项目实战的学习资料都有整理,送给每一位python小伙伴,希望能帮助你更了解python,学习python

    相关文章

      网友评论

        本文标题:使用python的scrapy来编写一个爬虫

        本文链接:https://www.haomeiwen.com/subject/ldslnqtx.html