您的位置:js12345金沙官网登入 > 网络编程 > 金沙澳门娱乐网址Scrapy爬取伯乐在线

金沙澳门娱乐网址Scrapy爬取伯乐在线

2019-10-02 10:00

Scrapy爬取伯乐在线文章

准备工作:

  • python环境,我是用Anaconda
  • Scrapy环境,上一篇文章提到过
  • MySQL,我们准备将爬取的数据保存到MySQL数据库中

首先通过scrapy命令创建项目

分析一下整个流程,可以分为两个部分。一,分析列表页面结构,获取每一篇文章的链接和图片地址以及下一个列表页地址。二,进入文章单页获取想要的内容数据。因此代码如果都写在一起显得非常臃肿,难以阅读。因此可以在parse函数处理第一部分逻辑,然后通过Request函数发送请求进行文章内容页的处理。

 def parse(self, response): """ 1获取文章列表页的url并交给scrapy下载后进行解析 2获取下一页url,交给scrapy下载,下载完成后交给parse """ #解析列表中所有文章url,并交给scrapy post_nodes = response.css("#archive .floated-thumb .post-thumb a") for post_node in post_nodes: image_url = post_node.css("img::attr.extract_first() post_url = post_node.css("::attr.extract_first() yield Request(url=parse.urljoin(response.url,post_url),meta={"front_image_url":image_url},callback=self.parse_detail) #提取下一页并交给scrapy next_urls = response.css(".next.page-numbers::attr.extract_first if next_urls: yield Request(url=parse.urljoin(response.url, next_urls), callback=self.parse)

本次爬取的内容为伯乐在线的文章,我们采取css方式来获取想要爬取的内容,具体css的使用方法我们在上一篇文章提到过,可以参看。

title = response.css('div.entry-header h1::text').extract_first()create_data = response.css('p.entry-meta-hide-on-mobile::text').extract_first.replace.strip()praise_nums = response.css('span.vote-post-up h10::text').extract_first()fav_nums = response.css(".bookmark-btn::text").extract_first()comment_nums = response.css("a[href='#article-comment'] span::text").extract_first()tag_list = response.css('p.entry-meta-hide-on-mobile a::text').extract()

我们可以发现文章的图片只是在列表页里面存在,如果是文章正文中,可能就不会出现,因此我们在处理文章链接的时候要同时处理文章的图片。这里用到了Request的一个变量meta,传递的内容为一个字典。meta={"front_image_url":image_url}

我们数据爬取的主要目的是从非结构的数据源转化为结构化的数据。但是提取数据之后,怎么将数据进行返回呢?数据以什么形式返回呢?这时候发现数据缺少了结构化的定义,为了将数据进行定义,方便格式化和处理,就用到了Item类。此时我们爬取的数据可以通过Item进行实例化。Scrapy发现yield的是一个Item类后,会将我们的Item路由到pipliens中,方便数据处理和保存。

class ArticleItem(scrapy.Item): title = scrapy.Field() create_date = scrapy.Field() url = scrapy.Field() url_object_id= scrapy.Field() front_image_url = scrapy.Field() front_image_path = scrapy.Field() praise_nums = scrapy.Field() comment_nums = scrapy.Field() fav_nums = scrapy.Field() tags = scrapy.Field() content = scrapy.Field()

scrapy提供了一个图片下载机制,只需要在settings.py文件夹下的ITEM_PIPELINES增加一句配置'scrapy.pipelines.images.ImagesPipeline':1,,意思是用scrapy提供的pipline中的images里面的ImagesPipeline。具体路径如下

金沙澳门娱乐网址 1image.png我们可以看到scrapy给我们提供了两个已经完成的pipeline,一个是图片的一个是媒体的。后面的数字1代表进入pipeline的优先级,越小代表优先级越高,在多个pipeline同时存在是应该注意。但是还有一个问题,pipeline怎么知道图片的地址呢?item中的字段那么多,又有哪一个该被传给pipeline呢?这个还需要在setting文件中配置

IMAGES_URLS_FIELD = "front_image_url"project_dir = os.path.abspath(os.path.dirname)IMAGES_STORE = os.path.join(project_dir,'images')

这样运行的时候会报一个错:raise ValueError('Missing scheme in request url %s' % self._url),这是因为pipline将IMAGES_URLS_FIELD = "front_image_url"按数组处理,但是我们item中的图片地址是一个值,而不是一个数组。我们可以将item中的值赋值的时候做一下修改:article_item['front_image_url'] = [front_image_url],在front_image_url上加了一个[],使其可迭代

我们如何把本地图片地址与文章关联起来呢?比如item中一个字段是图片的本地地址,我们应该怎么做呢?解决方法就是自己定义一个pipeline,继承图片下载的pipeline

class ArticleImagePipeline(ImagesPipeline): def item_completed(self, results, item, info): if "front_image_url" in item: for ok, value in results: image_file_path = value["path"] item["front_image_path"] = image_file_path return item

class JsonExporterPipleline: #调用scrapy提供的json export导出json文件 def __init__: self.file = open('articleexport.json', 'wb') self.exporter = JsonItemExporter(self.file, encoding="utf-8", ensure_ascii=False) self.exporter.start_exporting() def close_spider(self, spider): self.exporter.finish_exporting() self.file.close() def process_item(self, item, spider): self.exporter.export_item return item

DROP TABLE IF EXISTS `jobbole_article`;CREATE TABLE `jobbole_article` ( `title` varchar CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `create_date` date NULL DEFAULT NULL, `url` varchar CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `url_object_id` varchar CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `front_image_url` varchar CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `front_image_path` varchar CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `comment_nums` int NOT NULL DEFAULT 0, `fav_nums` int NOT NULL DEFAULT 0, `praise_nums` int NOT NULL DEFAULT 0, `tags` varchar CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `content` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL, PRIMARY KEY (`url_object_id`) USING BTREE) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;SET FOREIGN_KEY_CHECKS = 1;

使用pip install mysqlclient可以安装mysqlclient,如果是python2那么可以安装mysqldb,是一样的功能,API都相同。Linux下安装可能报错,如果是ubuntu需要执行sudo apt-get install libmysqlclient-dev,如果是centos可以执行sudo yum install python-devel mysql-devel

class MysqlPipeline: def __init__: self.conn = MySQLdb.connect('127.0.0.1', 'root', 'root', 'article_spider', charset='utf8', use_unicode=True) self.cursor = self.conn.cursor() def process_item(self, item, spider): insert_sql = 'INSERT INTO jobbole_article (`title`, `create_date`, `url`, `url_object_id`, `content`, `front_image_path`, `comment_nums`, `fav_nums`, `praise_nums`, `tags`) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)' self.cursor.execute(insert_sql, (item['title'], item['create_date'], item['url'], item['url_object_id'], item['content'], item["front_image_path"], item['comment_nums'], item['fav_nums'], item['praise_nums'], item['tags'])) self.conn.commit()
  • 上述的插入方法是同步插入,意味着这句话执行不结束下面的工作没法去做
  • 另外一个原因是spider的解析速度远大于插入数据的速度,这样到后期爬取的item越来越多,插入速度跟不上解析速度,就会造成堵塞
  • 另外异步插入可以根据不同的item定制插入语句,而不用写多个pipeline
class MysqlTwistedPipline: def __init__(self,dbpool): self.dbpool = dbpool @classmethod def from_settings(cls,settings): dbparms = dict( host = settings["MYSQL_HOST"], db = settings["MYSQL_DBNAME"], user = settings["MYSQL_USER"], passwd = settings["MYSQL_PASSWORD"], charset = 'utf8', cursorclass = MySQLdb.cursors.DictCursor, use_unicode = True ) dbpool = adbapi.ConnectionPool("MySQLdb",**dbparms) return cls def process_item(self,item,spider): #使用twisted将MYSQL插入编程异步执行 query = self.dbpool.runInteraction(self.do_insert,item,) query.addErrback(self.handle_error,item,spider)#处理异常 def handle_error(self,failure,item,spider): print #处理异步插入的异常 def do_insert(self,cursor,item): # 执行具体的插入 # 根据不同的item 构建不同的sql语句并插入到mysql中 insert_sql, params = item.get_insert_sql() cursor.execute(insert_sql, params)

既然已经有了item,那为什么要使用itemloader呢?我们可以看到不管是xpath,或者css,都是需要extract,然后可能还需要正则化处理,这样以后的维护工作会变得很困难。

# 通过item loader加载item front_image_url = response.meta.get("front_image_url", "") # 文章封面图 item_loader = ArticleItemLoader(item=JobBoleArticleItem(), response=response) # 通过css选择器将后面的指定规则进行解析。 item_loader.add_css("title", ".entry-header h1::text") item_loader.add_value("url", response.url) item_loader.add_value("url_object_id", get_md5(response.url)) item_loader.add_css("create_date", "p.entry-meta-hide-on-mobile::text") item_loader.add_value("front_image_url", [front_image_url]) item_loader.add_css("praise_nums", ".vote-post-up h10::text") item_loader.add_css("comment_nums", "a[href='#article-comment'] span::text") item_loader.add_css("fav_nums", ".bookmark-btn::text") item_loader.add_css("tags", "p.entry-meta-hide-on-mobile a::text") item_loader.add_css("content", "div.entry") # 调用这个方法来对规则进行解析生成item对象 article_item = item_loader.load_item() # 已经填充好了值调用yield传输至pipeline yield article_item

摘要:AI时代在我们生活中扮演着愈加重要的角色,其显著特征就是对海量数据的处理。所谓海量数据即大数据,我们首先获取到数据才能够挖掘其信息,达到AI层面的应用。而数据的存在形式,绝大多数是非结构化的,网页存储就是典型的非结构化数据。由此引出了网络爬虫技术,本文主要介绍Scrapy的原理和入门应用,以及本地化存储。

 号:864573496 群里有志同道合的小伙伴,互帮互助, 群里有不错的视频学习教程和PDF!

IDE:sublime

开发环境:win10+mysql5.0+navicat10.0.11

编程语言:python3.7+Anaconda4.4

技术选型:scrapy+requests

爬取目标: http://blog.jobbole.com/all-posts/

相关插件:python最近插件均可

建议豆瓣源镜像下载,可以提升下载速度。如:django

pip install -i https://pypi.doubanio.com/simple/ Django</pre>

scrapy 与 requests+beautifulsoup 区别

  • requests和beautifulsoup都是库,scrapy是框架
  • scrapy框架可以加入requests和beautifulsoup
  • scrapy基于twisted,性能的最大的优势
  • scrapy方便扩展,提供丰富功能
  • scrapy内置css和xpath selector非常方便,beautifulsoup速度慢

爬虫的作用

  • 搜索引擎 百度。google、垂直领域搜索引擎
  • 推荐引擎 今日头条
  • 机器学习的数据样本
  • 数据分析、舆情分析等

正则表达式

  • 特殊字符的提取 ^ $ . * ? + {2} {2,} {2,5}
  • ^ 表示开头
  • . 任意字符
    • 任意次数
  • $ 结尾

  • ? 非贪婪模式,提取第一个字符
    • 至少出现一次
  • {1} 出现一次

  • {3,} 出现3次以上
  • {2,5} 最少2次最多5次
  • 金沙澳门娱乐网址,| 或的关系
  • [] 满足任意一个都可以,[2435]任意 [0-9]区间非1
  • s 为空格 S非空格
  • w 匹配[A-Za-z0-9_]
  • W 反匹配[A-Za-z0-9_]
  • [u4E00-u9FA5] 汉字的匹配
  • d 匹配数字

爬虫去重策略

  • 将访问的url保存到数据库中,效率比较低
  • 将访问过的url保存到set中,只需要o的代价可以查询url1亿 2byte 50字符/1024/1024/1024=9G。一亿url就有9G内容,占用内存大
  • url经过md5等方式哈希编码后保存到set中,此时一亿url大约3G左右内容
  • 用bitmap方法,将访问过的url通过hash函数映射到某一位,存在冲突问题
  • bloomfilter方法对bitmap进行改进,多重hash函数降低冲突

Scrapy技术原理

架构图

金沙澳门娱乐网址 2image

  • Scrapy Engine: 负责Spider、ItemPipeline、Downloader、Scheduler中间的通讯,信号、数据传递等。
  • Scheduler: 它负责接受引擎发送过来的Request请求,并按照一定的方式进行整理排列,入队,当引擎需要时,交还给引擎。
  • Downloader:负责下载Scrapy Engine发送的所有Requests请求,并将其获取到的Responses交还给Scrapy Engine,由引擎交给Spider来处理,
  • Spider:它负责处理所有Responses,从中分析提取数据,获取Item字段需要的数据,并将需要跟进的URL提交给引擎,再次进入Scheduler.
  • Item Pipeline:它负责处理Spider中获取到的Item,并进行进行后期处理(详细分析、过滤、存储等)的地方。
  • Downloader Middlewares:你可以当作是一个可以自定义扩展下载功能的组件。
  • Spider Middlewares(Spider中间件):你可以理解为是一个可以自定扩展和操作引擎和Spider中间通信的功能组件(比如进入Spider的Responses;和从Spider出去的Requests)

制作 Scrapy 爬虫步骤:

1 新建项目 (scrapy startproject xxx):新建一个新的爬虫项目

2 明确目标 (编写items.py):明确你想要抓取的目标

3 制作爬虫 (spiders/xxspider.py):制作爬虫开始爬取网页

4 存储内容 (pipelines.py):设计管道存储爬取内容

scrapy安装和项目创建

1 安装scrapy,pip install scrapy

2 进入一个根目录文件夹下,创建Scrapy项目:scrapy startproject mySpider

3 其中, mySpider 为项目名称,可以看到将会创建一个 mySpider 文件夹,目录结构大致如下:下面来简单介绍一下各个主要文件的作用:

mySpider/scrapy.cfgmySpider/init.pyitems.pypipelines.pysettings.pyspiders/init.py

这些文件分别是:

  • scrapy.cfg: 项目的配置文件。
  • mySpider/: 项目的Python模块,将会从这里引用代码。
  • mySpider/items.py: 项目的目标文件。
  • mySpider/pipelines.py: 项目的管道文件。
  • mySpider/settings.py: 项目的设置文件。
  • mySpider/spiders/: 存储爬虫代码目录。

本文由js12345金沙官网登入发布于网络编程,转载请注明出处:金沙澳门娱乐网址Scrapy爬取伯乐在线

关键词: