这里是通过爬取伯乐在线的全部文章为例子,让自己先对scrapy进行一个整理的理解

该例子中的详细代码会放到我的github地址:https://github.com/pythonsite/spider/tree/master/jobboleSpider

注:这个文章并不会对详细的用法进行讲解,是为了让对scrapy各个功能有个了解,建立整体的印象。

在学习Scrapy框架之前,我们先通过一个实际的爬虫例子来理解,后面我们会对每个功能进行详细的理解。
这里的例子是爬取http://blog.jobbole.com/all-posts/ 伯乐在线的全部文章数据

分析要爬去的目标站信息

先看如下图,首先我们要获取下图中所有文章的连接,然后是进入每个文章连接爬取每个文章的详细内容。
每个文章中需要爬取文章标题,发表日期,以及标签,赞赏收藏,评论数,文章内容。

对于该爬虫的一个整体思路

我们对这个爬虫进行一个思路整理,通过如下图表示:

以上是我们对这个爬虫需求了解,下面我们通过scrapy爬取我们想要爬取的数据,下面我们先对scrapy进行一个简单的了解

Scrapy的初步认识

Scrapy使用了Twisted作为框架,Twisted有些特殊的地方是它是事件驱动的,并且比较适合异步的代码。对于会阻塞线程的操作包含访问文件、数据库或者Web、产生新的进程并需要处理新进程的输出(如运行shell命令)、执行系统层次操作的代码(如等待系统队列),Twisted提供了允许执行上面的操作但不会阻塞代码执行的方法。
scrapy的项目结构:

items.py 负责数据模型的建立,类似于实体类。
middlewares.py 自己定义的中间件。
pipelines.py 负责对spider返回数据的处理。
settings.py 负责对整个爬虫的配置。
spiders目录 负责存放继承自scrapy的爬虫类。
scrapy.cfg scrapy基础配置

那么如何创建上述的目录,通过下面命令:

  1. zhaofandeMBP:python_project zhaofan$ scrapy startproject test1
  2. New Scrapy project 'test1', using template directory '/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/site-packages/scrapy/templates/project', created in:
  3. /Users/zhaofan/Documents/python_project/test1
  4.  
  5. You can start your first spider with:
  6. cd test1
  7. scrapy genspider example example.com
  8. zhaofandeMBP:python_project zhaofan$
  9. zhaofandeMBP:test1 zhaofan$ scrapy genspider shSpider hshfy.sh.cn
  10. Created spider 'shSpider' using template 'basic' in module:
  11. test1.spiders.shSpider

相信上面这段话你肯定会觉得很无聊,所以直接分析爬虫代码。

代码的项目结构

items.py代码分析

items.py里存放的是我们要爬取数据的字段信息,代码如下:
我们分别要爬取的信息包括:文章标题,文件发布时间,文章url地址,url_object_id是我们会对地址进行md5加密,front_image_url 是文章下图片的url地址,front_image_path图片的存放路径

  1. class JoBoleArticleItem(scrapy.Item):
  2. title = scrapy.Field()
  3. create_date = scrapy.Field()
  4. url = scrapy.Field()
  5. url_object_id = scrapy.Field()
  6. front_image_url = scrapy.Field()
  7. front_image_path = scrapy.Field()
  8. praise_nums = scrapy.Field()
  9. fav_nums = scrapy.Field()
  10. comment_nums = scrapy.Field()
  11. tag = scrapy.Field()
  12. content = scrapy.Field()

spiders/Article.py代码分析

spiders目录下的Article.py为主要的爬虫代码,包括了对页面的请求以及页面的处理,这里有几个知识点需要注意:
这些知识点我会在后面详细写一个文章整理,这里先有一个初步的印象。

1. 我们爬取的页面时http://blog.jobbole.com/all-posts/,所以parse的response,返回的是这个页面的信息,但是我们这个时候需要的是获取每个文章的地址继续访问,这里就用到了yield Request()这种用法,可以把获取到文章的url地址继续传递进来再次进行请求。
2. scrapy提供了response.css这种的css选择器以及response.xpath的xpath选择器方法,我们可以根据自己的需求获取我们想要的字段信息

  1. class ArticleSpider(scrapy.Spider):
  2. name = "Article"
  3. allowed_domains = ["blog.jobbole.com"]
  4. start_urls = ['http://blog.jobbole.com/all-posts/']
  5.  
  6. def parse(self, response):
  7. '''
  8. 1.获取文章列表也中具体文章url,并交给scrapy进行下载后并进行解析
  9. 2.获取下一页的url并交给scrapy进行下载,下载完成后,交给parse
  10. :param response:
  11. :return:
  12. '''
  13. #解析列表页中所有文章的url,并交给scrapy下载后进行解析
  14. post_nodes = response.css("#archive .floated-thumb .post-thumb a")
  15. for post_node in post_nodes:
  16. #image_url是图片的地址
  17. image_url = post_node.css("img::attr(src)").extract_first("")
  18. post_url = post_node.css("::attr(href)").extract_first("")
  19. #这里通过meta参数将图片的url传递进来,这里用parse.urljoin的好处是如果有域名我前面的response.url不生效
  20. # 如果没有就会把response.url和post_url做拼接
  21. yield Request(url=parse.urljoin(response.url,post_url),meta={"front_image_url":parse.urljoin(response.url,image_url)},callback=self.parse_detail)
  22.  
  23. #提取下一页并交给scrapy下载
  24. next_url = response.css(".next.page-numbers::attr(href)").extract_first("")
  25. if next_url:
  26. yield Request(url=next_url,callback=self.parse)
  27.  
  28. def parse_detail(self,response):
  29. '''
  30. 获取文章的详细内容
  31. :param response:
  32. :return:
  33. '''
  34. article_item = JoBoleArticleItem()
  35.  
  36. front_image_url = response.meta.get("front_image_url","") #文章封面图地址
  37. title = response.xpath('//div[@class="entry-header"]/h1/text()').extract_first()
  38.  
  39. create_date = response.xpath('//p[@class="entry-meta-hide-on-mobile"]/text()').extract()[0].strip().split()[0]
  40.  
  41. tag_list = response.xpath('//p[@class="entry-meta-hide-on-mobile"]/a/text()').extract()
  42. tag_list = [element for element in tag_list if not element.strip().endswith("评论")]
  43. tag =",".join(tag_list)
  44. praise_nums = response.xpath('//span[contains(@class,"vote-post-up")]/h10/text()').extract()
  45. if len(praise_nums) == 0:
  46. praise_nums = 0
  47. else:
  48. praise_nums = int(praise_nums[0])
  49. fav_nums = response.xpath('//span[contains(@class,"bookmark-btn")]/text()').extract()[0]
  50. match_re = re.match(".*(\d+).*",fav_nums)
  51. if match_re:
  52. fav_nums = int(match_re.group(1))
  53. else:
  54. fav_nums = 0
  55.  
  56. comment_nums =response.xpath("//a[@href='#article-comment']/span/text()").extract()[0]
  57. match_com = re.match(".*(\d+).*",comment_nums)
  58. if match_com:
  59. comment_nums = int(match_com.group(1))
  60. else:
  61. comment_nums=0
  62.  
  63. content = response.xpath('//div[@class="entry"]').extract()[0]
  64.  
  65. article_item["url_object_id"] = get_md5(response.url) #这里对地址进行了md5变成定长
  66. article_item["title"] = title
  67. article_item["url"] = response.url
  68. try:
  69. create_date = datetime.datetime.strptime(create_date,'%Y/%m/%d').date()
  70. except Exception as e:
  71. create_date = datetime.datetime.now().date()
  72.  
  73. article_item["create_date"] = create_date
  74. article_item["front_image_url"] = [front_image_url]
  75. article_item["praise_nums"] = int(praise_nums)
  76. article_item["fav_nums"] = fav_nums
  77. article_item["comment_nums"] = comment_nums
  78. article_item["tag"] = tag
  79. article_item['content'] = content
  80.  
  81. yield article_item

pipeline中代码的分析

pipeline主要是对spiders中爬虫的返回的数据的处理,这里我们可以让写入到数据库,也可以让写入到文件等等。
下面代码中主要包括的写入到json文件以及写入到数据库,包括异步插入到数据库,还有图片的处理,这里我们可以定义各种我们需要的pipeline,当然这里我们不同的pipeline是有一定的顺序的,需要的设置是在settings配置文件中,如下,后面的数字表示的是优先级,数字越小优先级越高。

  1. class JobbolespiderPipeline(object):
  2. def process_item(self, item, spider):
  3. return item
  4.  
  5. class JsonWithEncodingPipeline(object):
  6. '''
  7. 返回json数据到文件
  8. '''
  9. def __init__(self):
  10. self.file = codecs.open("article.json",'w',encoding="utf-8")
  11.  
  12. def process_item(self, item, spider):
  13. lines = json.dumps(dict(item),ensure_ascii=False) + "\n"
  14. self.file.write(lines)
  15. return item
  16.  
  17. def spider_closed(self,spider):
  18. self.file.close()
  19.  
  20. class MysqlPipeline(object):
  21. '''
  22. 插入mysql数据库
  23. '''
  24. def __init__(self):
  25. self.conn =pymysql.connect(host='192.168.1.19',port=3306,user='root',passwd='',db='article_spider',use_unicode=True, charset="utf8")
  26. self.cursor = self.conn.cursor()
  27.  
  28. def process_item(self,item,spider):
  29. insert_sql = '''
  30. insert into jobbole_article(title,create_date,url,url_object_id,front_image_url,front_image_path,comment_nums,fav_nums,praise_nums,tag,content) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)
  31. '''
  32.  
  33. self.cursor.execute(insert_sql,(item["title"],item["create_date"],item["url"],item["url_object_id"],item["front_image_url"],item["front_image_path"],item["comment_nums"],item["fav_nums"],item["praise_nums"],item["tag"],item["content"]))
  34. self.conn.commit()
  35.  
  36. class MysqlTwistedPipline(object):
  37. '''
  38. 采用异步的方式插入数据
  39. '''
  40. def __init__(self,dbpool):
  41. self.dbpool = dbpool
  42.  
  43. @classmethod
  44. def from_settings(cls,settings):
  45. dbparms = dict(
  46. host = settings["MYSQL_HOST"],
  47. port = settings["MYSQL_PORT"],
  48. user = settings["MYSQL_USER"],
  49. passwd = settings["MYSQL_PASSWD"],
  50. db = settings["MYSQL_DB"],
  51. use_unicode = True,
  52. charset="utf8",
  53. )
  54. dbpool = adbapi.ConnectionPool("pymysql",**dbparms)
  55. return cls(dbpool)
  56. def process_item(self,item,spider):
  57. '''
  58. 使用twisted将mysql插入变成异步
  59. :param item:
  60. :param spider:
  61. :return:
  62. '''
  63. query = self.dbpool.runInteraction(self.do_insert,item)
  64. query.addErrback(self.handle_error)
  65.  
  66. def handle_error(self,failure):
  67. #处理异步插入的异常
  68. print(failure)
  69.  
  70. def do_insert(self,cursor,item):
  71. #具体插入数据
  72. insert_sql = '''
  73. insert into jobbole_article(title,create_date,url,url_object_id,front_image_url,front_image_path,comment_nums,fav_nums,praise_nums,tag,content) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)
  74. '''
  75. cursor.execute(insert_sql,(item["title"],item["create_date"],item["url"],item["url_object_id"],item["front_image_url"],item["front_image_path"],item["comment_nums"],item["fav_nums"],item["praise_nums"],item["tag"],item["content"]))
  76.  
  77. class ArticleImagePipeline(ImagesPipeline):
  78. '''
  79. 对图片的处理
  80. '''
  81. def item_completed(self, results, item, info):
  82.  
  83. for ok ,value in results:
  84. if ok:
  85. image_file_path = value["path"]
  86. item['front_image_path'] = image_file_path
  87. else:
  88. item['front_image_path'] = ""
  89.  
  90. return item

Python爬虫从入门到放弃(十一)之 Scrapy框架整体的一个了解的更多相关文章

  1. python爬虫从入门到放弃前奏之学习方法

    首谈方法 最近在整理爬虫系列的博客,但是当整理几篇之后,发现一个问题,不管学习任何内容,其实方法是最重要的,按照我之前写的博客内容,其实学起来还是很点枯燥不能解决传统学习过程中的几个问题: 这个是普通 ...

  2. Python爬虫从入门到放弃(十二)之 Scrapy框架的架构和原理

    这一篇文章主要是为了对scrapy框架的工作流程以及各个组件功能的介绍 Scrapy目前已经可以很好的在python3上运行Scrapy使用了Twisted作为框架,Twisted有些特殊的地方是它是 ...

  3. Python爬虫从入门到放弃(二十四)之 Scrapy登录知乎

    因为现在很多网站为了限制爬虫,设置了为只有登录才能看更多的内容,不登录只能看到部分内容,这也是一种反爬虫的手段,所以这个文章通过模拟登录知乎来作为例子,演示如何通过scrapy登录知乎 在通过scra ...

  4. Python爬虫从入门到放弃(二十一)之 Scrapy分布式部署

    按照上一篇文章中我们将代码放到远程主机是通过拷贝或者git的方式,但是如果考虑到我们又多台远程主机的情况,这种方式就比较麻烦,那有没有好用的方法呢?这里其实可以通过scrapyd,下面是这个scrap ...

  5. python爬虫从入门到放弃(三)之 Urllib库的基本使用

    官方文档地址:https://docs.python.org/3/library/urllib.html 什么是Urllib Urllib是python内置的HTTP请求库包括以下模块urllib.r ...

  6. python爬虫从入门到放弃(四)之 Requests库的基本使用

    什么是Requests Requests是用python语言基于urllib编写的,采用的是Apache2 Licensed开源协议的HTTP库如果你看过上篇文章关于urllib库的使用,你会发现,其 ...

  7. python爬虫从入门到放弃(六)之 BeautifulSoup库的使用

    上一篇文章的正则,其实对很多人来说用起来是不方便的,加上需要记很多规则,所以用起来不是特别熟练,而这节我们提到的beautifulsoup就是一个非常强大的工具,爬虫利器. beautifulSoup ...

  8. python爬虫从入门到放弃(八)之 Selenium库的使用

    一.什么是Selenium selenium 是一套完整的web应用程序测试系统,包含了测试的录制(selenium IDE),编写及运行(Selenium Remote Control)和测试的并行 ...

  9. Python爬虫从入门到放弃(十三)之 Scrapy框架的命令行详解

    这篇文章主要是对的scrapy命令行使用的一个介绍 创建爬虫项目 scrapy startproject 项目名例子如下: localhost:spider zhaofan$ scrapy start ...

随机推荐

  1. 2017-5-22 ASP六大 内置对象

    ASP内置对象:提供内建对象,这些对象使用户更容易收集通过浏览器请 求发送的信息.响应浏览器以及存储用户信息(如用户首选项). 1.Request  --- 获取请求对象 获取通过地址栏传值过来的对象 ...

  2. 导入数据到mysql服务器上,报错,以及停止的解决办法

    两次都是因为磁盘不够,这次得以解决: =>下面是可能会用到的命令行: 1.查看磁盘占用大小: df -hl 发现:   从上面看出来,根目录 / 下的东西沾满了  /dev/sda1  但是 / ...

  3. VR全景智慧城市:360全景市场需要背景及其优势~

    VR元年已过,VR项目.VR创业潮转为理性,VR行业分为两个方向:硬件和内容. VR全景,又被称为3D实景,是一种新兴的富媒体技术,其与视频,声音,图片等传统的流媒体大的区别是"可操作,可交 ...

  4. 修改cms版权等等信息

    目的:为DedeCMS换上精美多样的提示信息窗口 用到的开源项目:DedeCMS,artdialog 步骤: 1.下载include.rar文件完成后,解压得到2个php文件和一个使用说明文件,将ph ...

  5. POI使用:用poi接口不区分xls/xlsx格式解析Excel文档(41种日期格式解析方法,5种公式结果类型解析方法,3种常用数值类型精度控制办法)

    一.使用poi解析excel文档 注:全部采用poi接口进行解析,不需要区分xls.xlsx格式,不需要判断文档类型. poi中的日期格式判断仅支持欧美日期习惯,对国内的日期格式并不支持判断,怎么办? ...

  6. module.exports,exports,export和export default,import与require区别与联系【原创】

    还在为module.exports.exports.export和export default,import和require区别与联系发愁吗,这一篇基本就够了! 一.首先搞清楚一个基本问题: modu ...

  7. JAVA优雅停机的实现

    最近在项目中需要写一个数据转换引擎服务,每过5分钟同步一次数据.具体实现是启动engine server后会初始化一个ScheduledExecutorService和一个ThreadPoolExec ...

  8. java架构师负载均衡、高并发、nginx优化、tomcat集群、异步性能优化、Dubbo分布式、Redis持久化、ActiveMQ中间件、Netty互联网、spring大型分布式项目实战视频教程百度网盘

    15套Java架构师详情 * { font-family: "Microsoft YaHei" !important } h1 { background-color: #006; ...

  9. jquery让页面滚动到底部

    function scrollToEnd(){//滚动到底部 var h = $(document).height()-$(window).height(); $(document).scrollTo ...

  10. js将字符串转化成函数:eval(logOutCallbackFun+"()");

    js将字符串转化成函数:eval(logOutCallbackFun+"()");