应用Scrapy爬取所有虎扑用户详细音信并存至MongoDB

迎接大家前往腾讯云技术社区,获取更多腾讯海量技术实施干货哦~

品味最初的爬取

接下去大家什么代码也不修改,执行爬取,运行如下命令:

scrapy crawl zhihu

你会发觉爬取结果会油可是生这么的一个不当:

500 Internal Server Error

做客新浪得到的状态码是500,那讲明爬取并不曾得逞,其实那是因为我们从没投入请求头,和讯识别User-Agent发觉不是浏览器,就重回错误的响应了。

之所以接下去的一步大家要求进入请求 headers 音讯,你可以在 Request
的参数里加,也足以在 spider
里面的custom_settings里面加,当然最简便易行的办法其实在全局 settings
里面加了。

咱们开拓settings.py文件,取消DEFAULT_REQUEST_HEADERS的诠释,加入如下的内容:

DEFAULT_REQUEST_HEADERS = {
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36'
}

那些是为您的请求添加请求头,若是您未曾安装 headers
的话,它就会利用这几个请求头请求,添加了User-Agent新闻,所以这么我们的爬虫就可以装作浏览器了。

接下去重新运行爬虫。

scrapy crawl zhihu

那会儿你就会发觉赢得的归来状态码就像常了。

涸泽而渔了那几个题目,我们接下去就足以分析页面逻辑来规范达成爬虫了。

MongoDB的LBS服务原理

MongoDB中使用2d_index
或2d_sphere_index来成立地理地方索引(geoIndex),两者反差不大,上面大家以2d_index为例来介绍。

一.2D索引的创始与行使

db.coll.createIndex({"lag":"2d"}, {"bits":int}))
通过上述命令来创建一个2d索引,索引的精度通过bits来指定,bits越大,索引的精度就越高。更大的bits带来的插入的overhead可以忽略不计
db.runCommand({
geoNear: tableName,
maxDistance: 0.0001567855942887398,
distanceMultiplier: 6378137.0,
num: 30,
near: [ 113.8679388183982, 22.58905429302385 ],
spherical: true|false})

经过上述命令来询问一个索引,其中spherical:true|false
表示应该如何领悟成立的2d索引,false表示将引得明白为平面2d索引,true表示将引得了然为球面经纬度索引。那点相比好玩,一个2d索引得以表明三种意义,而区其他意义是在询问时被通晓的,而不是在目录创设时。

二.2D索引的论争

MongoDB 使用GeoHash的技术来构建2d索引(见wiki geohash
文字链 https://en.wikipedia.org/wiki/Geohash )。MongoDB使用平面四叉树划分的不二法门来生成GeoHashId,每条记下有一个GeoHashId,通过GeoHashId->RecordId的索引映射方式存储在Btree中。

图片 1

很精晓的,一个2bits的精度能把平面分为4个grid,一个4bits的精度能把平面分为16个grid。
2d索引的默许精度是长宽各为26,索引把地球分为(226)(226)块,每一块的边长估计为2PI6371000/(1<<26)
= 0.57 米。
mongodb的官网上说的60cm的精度就是这么臆想出来的。
By default, a 2d index on legacy coordinate pairs uses 26 bits of
precision, which isroughly equivalent to 2 feet or 60 centimeters of
precision using the default range of-180 to 180。

 

三.2D索引在Mongodb中的存储

上面我们讲到Mongodb使用平面四叉树的不二法门测算Geohash。事实上,平面四叉树仅存在于运算的经过中,在事实上存储中并不会被选拔到。

插入
对此一个经纬度坐标[x,y],MongoDb总结出该坐标在2d平面内的grid编号,该号码为是一个52bit的int64类型,该项目被看做btree的key,由此实际数据是比照
{GeoHashId->RecordValue}的不二法门被插入到btree中的。

查询
对于geo2D索引的查询,常用的有geoNear和geoWithin二种。geoNear查找距离某个点以来的N个点的坐标并赶回,该必要可以说是整合了LBS服务的底蕴(陌陌,滴滴,摩拜),geoWithin是查询一个多方形内的所有点并赶回。我们重点介绍使用最普遍的geoNear查询。

geoNear的询问进度,查询语句如下

db.runCommand(
{
geoNear: "places", //table Name
near: [ -73.9667, 40.78 ] , // central point
spherical: true, // treat the index as a spherical index
query: { category: "public" } // filters
maxDistance: 0.0001531 // distance in about one kilometer
}

geoNear可以驾驭为一个从初始点先河的不止向外扩散的环形搜索进度。如下图所示:

图片 2

由于圆自己的性质,外环的任意点到圆心的距离一定大于内环任意点到圆心的离开,所以以圆
环举办伸张迭代的裨益是:

1)减弱须求排序比较的点的个数;
2)可以急速发现满足条件的点从而重临,避免不须要的摸索。

MongoDB在完毕的细节中,如若内环搜索到的罗列过少,圆环每一回增添的步长会倍增

parse_follows

接下去大家处理一下关心列表,首先也是解析response的文书,然后要做两件事:

  • 通过关注列表的每一个用户,对每一个用户发起呼吁,获取其详细信息。

  • 拍卖分页,判断 paging 内容,获取下一页关心列表。

于是在那里将parse_follows改写如下:

results = json.loads(response.text)

if 'data' in results.keys():
    for result in results.get('data'):
        yield Request(self.user_url.format(user=result.get('url_token'), include=self.user_query),
                      self.parse_user)

if 'paging' in results.keys() and results.get('paging').get('is_end') == False:
    next_page = results.get('paging').get('next')
    yield Request(next_page,
                  self.parse_follows)

诸如此类,全体代码如下:

# -*- coding: utf-8 -*-
import json

from scrapy import Spider, Request
from zhihuuser.items import UserItem


class ZhihuSpider(Spider):
    name = "zhihu"
    allowed_domains = ["www.zhihu.com"]
    user_url = 'https://www.zhihu.com/api/v4/members/{user}?include={include}'
    follows_url = 'https://www.zhihu.com/api/v4/members/{user}/followees?include={include}&offset={offset}&limit={limit}'
    start_user = 'excited-vczh'
    user_query = 'locations,employments,gender,educations,business,voteup_count,thanked_Count,follower_count,following_count,cover_url,following_topic_count,following_question_count,following_favlists_count,following_columns_count,answer_count,articles_count,pins_count,question_count,commercial_question_count,favorite_count,favorited_count,logs_count,marked_answers_count,marked_answers_text,message_thread_token,account_status,is_active,is_force_renamed,is_bind_sina,sina_weibo_url,sina_weibo_name,show_sina_weibo,is_blocking,is_blocked,is_following,is_followed,mutual_followees_count,vote_to_count,vote_from_count,thank_to_count,thank_from_count,thanked_count,description,hosted_live_count,participated_live_count,allow_message,industry_category,org_name,org_homepage,badge[?(type=best_answerer)].topics'
    follows_query = 'data[*].answer_count,articles_count,gender,follower_count,is_followed,is_following,badge[?(type=best_answerer)].topics'

    def start_requests(self):
        yield Request(self.user_url.format(user=self.start_user, include=self.user_query), self.parse_user)
        yield Request(self.follows_url.format(user=self.start_user, include=self.follows_query, limit=20, offset=0),
                      self.parse_follows)

    def parse_user(self, response):
        result = json.loads(response.text)
        item = UserItem()

        for field in item.fields:
            if field in result.keys():
                item[field] = result.get(field)
        yield item

        yield Request(
            self.follows_url.format(user=result.get('url_token'), include=self.follows_query, limit=20, offset=0),
            self.parse_follows)

    def parse_follows(self, response):
        results = json.loads(response.text)

        if 'data' in results.keys():
            for result in results.get('data'):
                yield Request(self.user_url.format(user=result.get('url_token'), include=self.user_query),
                              self.parse_user)

        if 'paging' in results.keys() and results.get('paging').get('is_end') == False:
            next_page = results.get('paging').get('next')
            yield Request(next_page,
                          self.parse_follows)

这么大家就完事了得到用户基本新闻,然后递归获取关切列表进一步请求了。

再一次运行爬虫,可以窥见脚下早已得以兑现循环递归爬取了。

总结

MongoDB原生的geoNear接口是境内各大LBS应用的主流选拔。原生MongoDB在点集稠密的状态下,geoNear接口功能会急剧下跌,单机甚至不到1000QPS。腾讯云MongoDB团队对此开展了绵绵的优化,在不影响效果的前提下,geoNear的频率有10倍以上的擢升,为大家的客户如摩拜提供了暴力的扶助,同时比较Redis3.2也有较大的习性优势。

followers

上边大家贯彻了通过取得关切列表完结爬取循环,那这里少不了的还有粉丝列表,经过分析后发现粉丝列表的
api 也类似,只但是把 followee 换成了
follower,其余的完全相同,所以我们根据同等的逻辑添加 followers
相关音讯,

说到底spider代码如下:

# -*- coding: utf-8 -*-
import json

from scrapy import Spider, Request
from zhihuuser.items import UserItem


class ZhihuSpider(Spider):
    name = "zhihu"
    allowed_domains = ["www.zhihu.com"]
    user_url = 'https://www.zhihu.com/api/v4/members/{user}?include={include}'
    follows_url = 'https://www.zhihu.com/api/v4/members/{user}/followees?include={include}&offset={offset}&limit={limit}'
    followers_url = 'https://www.zhihu.com/api/v4/members/{user}/followers?include={include}&offset={offset}&limit={limit}'
    start_user = 'excited-vczh'
    user_query = 'locations,employments,gender,educations,business,voteup_count,thanked_Count,follower_count,following_count,cover_url,following_topic_count,following_question_count,following_favlists_count,following_columns_count,answer_count,articles_count,pins_count,question_count,commercial_question_count,favorite_count,favorited_count,logs_count,marked_answers_count,marked_answers_text,message_thread_token,account_status,is_active,is_force_renamed,is_bind_sina,sina_weibo_url,sina_weibo_name,show_sina_weibo,is_blocking,is_blocked,is_following,is_followed,mutual_followees_count,vote_to_count,vote_from_count,thank_to_count,thank_from_count,thanked_count,description,hosted_live_count,participated_live_count,allow_message,industry_category,org_name,org_homepage,badge[?(type=best_answerer)].topics'
    follows_query = 'data[*].answer_count,articles_count,gender,follower_count,is_followed,is_following,badge[?(type=best_answerer)].topics'
    followers_query = 'data[*].answer_count,articles_count,gender,follower_count,is_followed,is_following,badge[?(type=best_answerer)].topics'

    def start_requests(self):
        yield Request(self.user_url.format(user=self.start_user, include=self.user_query), self.parse_user)
        yield Request(self.follows_url.format(user=self.start_user, include=self.follows_query, limit=20, offset=0),
                      self.parse_follows)
        yield Request(self.followers_url.format(user=self.start_user, include=self.followers_query, limit=20, offset=0),
                      self.parse_followers)

    def parse_user(self, response):
        result = json.loads(response.text)
        item = UserItem()

        for field in item.fields:
            if field in result.keys():
                item[field] = result.get(field)
        yield item

        yield Request(
            self.follows_url.format(user=result.get('url_token'), include=self.follows_query, limit=20, offset=0),
            self.parse_follows)

        yield Request(
            self.followers_url.format(user=result.get('url_token'), include=self.followers_query, limit=20, offset=0),
            self.parse_followers)

    def parse_follows(self, response):
        results = json.loads(response.text)

        if 'data' in results.keys():
            for result in results.get('data'):
                yield Request(self.user_url.format(user=result.get('url_token'), include=self.user_query),
                              self.parse_user)

        if 'paging' in results.keys() and results.get('paging').get('is_end') == False:
            next_page = results.get('paging').get('next')
            yield Request(next_page,
                          self.parse_follows)

    def parse_followers(self, response):
        results = json.loads(response.text)

        if 'data' in results.keys():
            for result in results.get('data'):
                yield Request(self.user_url.format(user=result.get('url_token'), include=self.user_query),
                              self.parse_user)

        if 'paging' in results.keys() and results.get('paging').get('is_end') == False:
            next_page = results.get('paging').get('next')
            yield Request(next_page,
                          self.parse_followers)

亟需改变的职位有

  • start_requests中间添加yield followers音讯

  • parse_user里头里面添加yield followers信息

  • parse_followers做相应的的抓取详情请求和翻页

如此一来,spider
就水到渠成了,那样大家就可以达成通过社交网络递归的爬取,把用户详情都爬下来。

和Redis3.2的对比

Redis3.2也投入了地理地方查询的效果,我们也将开源Redis和云数据库MongoDB进行自查自纠。
Redis使用形式:GEORADIUS appname 120.993965 31.449034 500 m count 30
asc。在凝聚数据集场景下,使用腾讯云MongoDB和开源的Redis进行了性能相比。下图是在凝聚数据集上,在24核CPU机器上,MongoDB单实例与Redis单实例的测试对照。要求注意的是Redis本身是单线程的内存缓存数据库。MongoDB是三十二线程的高可用持久化的数据库,两者的行使情形有较大分化。

 

图片 3

本节目的

本节要得以完成的始末有:

  • 从一个大V用户初叶,通过递归抓取粉丝列表和关注列表,达成天涯论坛所有用户的详细音讯的抓取。

  • 将抓取到的结果存储到 MongoDB,并开展去重操作。

LBS业务特点

以共享单车服务为例,LBS业务有所2个特点,分别是光阴周期性和坐标分布不均匀。

parse_user

接下去大家处理一下用户中央音讯,首先大家查阅一下接口音信会再次回到一些什么数据。

图片 4

可以见见重临的结果足够全,在此地大家一贯声喜宝个Item全保存下就好了。

在 items 里新声爱他美(Aptamil)个 UserItem

from scrapy import Item, Field

class UserItem(Item):
    # define the fields for your item here like:
    id = Field()
    name = Field()
    avatar_url = Field()
    headline = Field()
    description = Field()
    url = Field()
    url_token = Field()
    gender = Field()
    cover_url = Field()
    type = Field()
    badge = Field()

    answer_count = Field()
    articles_count = Field()
    commercial_question_count = Field()
    favorite_count = Field()
    favorited_count = Field()
    follower_count = Field()
    following_columns_count = Field()
    following_count = Field()
    pins_count = Field()
    question_count = Field()
    thank_from_count = Field()
    thank_to_count = Field()
    thanked_count = Field()
    vote_from_count = Field()
    vote_to_count = Field()
    voteup_count = Field()
    following_favlists_count = Field()
    following_question_count = Field()
    following_topic_count = Field()
    marked_answers_count = Field()
    mutual_followees_count = Field()
    hosted_live_count = Field()
    participated_live_count = Field()

    locations = Field()
    educations = Field()
    employments = Field()

因此在条分缕析方法里面我们分析得到的 response 内容,然后转为 json
对象,然后依次判断字段是还是不是存在,赋值就好了。

result = json.loads(response.text)
item = UserItem()
for field in item.fields:
    if field in result.keys():
        item[field] = result.get(field)
yield item

赢得 item 后经过 yield 再次回到就好了。

那样保存用户中央新闻就到位了。

接下去大家还索要在此间收获那么些用户的关心列表,所以我们须要再重新发起一个赢得关心列表的
request

parse_user背后再添加如下代码:

yield Request(
            self.follows_url.format(user=result.get('url_token'), include=self.follows_query, limit=20, offset=0),
            self.parse_follows)

那样大家又变化了得到该用户关注列表的央浼。

二.坐标分布不均匀

坐大巴的上班族,倘使注意可能会发现,在上班早高峰时,客车周围摆满了共享单车,而下班
时段,地铁周围的共享单车数量万分少。如下图,经纬度(121,31.44)附近集中了99%上述
的坐标。此外,一些独特事件也会导致点的分布不均匀,例如卡拉奇湾花园在非正规家沐日涌入多量的客户,同时那么些地段也会排放大批量的车子。部分地方单车量卓殊集中,而其他地域就相当稀疏。

图片 5

禁止ROBOTSTXT_OBEY

接下去你须求开辟settings.py文件,将ROBOTSTXT_OBEY修改为 False。

ROBOTSTXT_OBEY = False

它默许为True,就是要信守robots.txt 的规则,那么robots.txt是个怎么样事物吗?

深远浅出的话,robots.txt是比照 罗布(Rob)ot
协议的一个文本,它保存在网站的服务器中,它的出力是,告诉搜索引擎爬虫,本网站哪些目录下的网页
不期望
你举行爬取收录。在Scrapy启动后,会在第一时间访问网站的robots.txt 文件,然后决定该网站的爬取范围。

本来,大家并不是在做搜索引擎,而且在少数意况下大家想要获取的始末恰恰是被robots.txt 所禁止访问的。所以,某些时候,大家即将将此布局项设置为
False ,拒绝坚守 罗布(Rob)ot协和 !

于是在此地设置为 False
。当然可能这次爬取不自然会被它界定,但是我们一般的话会率先选拔禁止它。

问题的解决

题目大家曾经了解了,大家对此的优化措施是控制每一圈的搜索量,为此大家为geoNear命令伸张了七个参数,将其扩散NearStage中。hintCorrectNum可以决定结果质量的下限,重返的前N个肯定是最靠近中央点的N个点。hintScan用以控制扫描集的分寸的上限。

hintScan: 已经围观的点集大小大于hintScan后,做模糊处理。
hintCorrectNum:已经回来的结果数当先hintCorrectNum后,做模糊处理。

图片 6

 

该优化本质上是经过捐躯质地来不久回到结果。对于国内多数LBS服务以来,完全的严苛如今并不是不可或缺的。且可以经过控制参数得到严厉如今的效率。在摸索进程中,密集的点落到一个环内,本身距离相差也不会不大。该优化在上线后,将部分大客户的MongoDB性能上限从单机1000QPS提高了10倍到10000QPS以上。

图片 7

图片 8

OAuth

它是Open Authorization的缩写。

OAUTH_token:OAUTH进展到终极一步获得的一个“令牌”,通过此“令牌”请求,就足以去拥有资源的网站抓取任意有权力可以被抓取的资源。

在此间自己新浪并不曾登陆,那里的OAuth值是

oauth c3cef7c66a1843f8b3a9e6a1e3160e20

透过自身长时间的体察,这些一贯不会改变,所以能够长时间使用,大家将它配备到DEFAULT_REQUEST_HEADERS里,那样它就成为了:

DEFAULT_REQUEST_HEADERS = {
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',
    'authorization': 'oauth c3cef7c66a1843f8b3a9e6a1e3160e20',
}

接下去即便大家重新运行爬虫,就可以发现能够健康爬取了。

一.时间周期性

高峰期与低谷期的QPS量相差显然,并且高峰期和低峰期的大运点相对稳定。

图片 9

作者 :崔庆才

乘机国内服务共享化的狂潮普及,共享单车,共享雨伞,共享充电宝等各类劳动如成千成万,随之而来的LBS服务定位问题成为了后端服务的一个挑衅。MongoDB对LBS查询的支持比较协调,也是各大LBS服务商的首选数据库。腾讯云MongoDB团队在营业中窥见,原生MongoDB在LBS服务场所下有较大的属性瓶颈,经腾讯云团队专业的一贯分析与优化后,云MongoDB在LBS服务的归结性能上,有10倍以上的晋级。
腾讯云MongoDB提供的好好综合性能,为国内各大LBS服务商,例如摩拜单车等,提供了精锐的有限协助。

连带推荐

Python操作Redis –
云爬虫初探

腾讯云主机Python3环境设置PySpider爬虫框架进度
腾讯云上PhantomJS用法示例


 

此文已由作者授权腾讯云技术社区表露,转载请阐明作品出处
原文链接:https://www.qcloud.com/community/article/327315
获得越多腾讯海量技术实施干货,欢迎大家前往腾讯云技术社区

推介阅读

邹方明:看腾讯云怎么着架构海量存储系统
熊普江:
BGP网络架构助力开发者急忙构建、优化工作

唐良:云端架构给电商行业拉动立异力
黄荣奎:如何快捷、便捷开发小程序
电商行业开发者怎么样按照云端构建业务?腾讯云+将来峰会上这么说


 

此文已由作者授权腾讯云技术社区宣布,转发请注脚文章出处
原文链接:https://cloud.tencent.com/community/article/723875

本节享受一下爬取今日头条用户拥有用户音讯的 Scrapy 爬虫实战。

MongoDB LBS服务遭逢的题目

一对大客户在选用MongoDB的geoNear功用查找附近的目的时,常常会发出慢查询较多的问题,早高峰压力是低谷时段的10-20倍,坐标不均匀的动静慢查询严重,濒临雪崩。开首分析发现,这几个查询扫描了过多的点集。

一般来说图,查找500米范围内,距离近期的10条记下,开销了500ms,扫描了24000+的笔录。类似的慢查询占据了高峰期5%左右的查询量

图片 10

测试环境复现与稳定

排查数据库的性能问题,首要从锁等待,IO等待,CPU消耗三封面分析。上面的截图扫描了过多的笔录,直觉上是CPU或者IO消耗性的瓶颈。为了严俊起见,大家在测试环境复现后,发现慢日志中无显明的timeAcquiringMicroseconds项排除了MongoDB执行层面的锁竞争问题,并接纳较大内存的机器使得数据常驻内存,发现上述用例依然必要200ms以上的执行时间。10核的CPU资源针对截图中的case,只能支持50QPS。

图片 11

图片 12

为啥扫描集如此大

上面大家说过,MongoDB搜索距离近日的点的长河是一个环形增加的进度,即使内环满意条件的点不够多,每便的增添半径都会加倍。由此在碰着内环点稀少,外环有密集点的气象时,不难陷入BadCase。如下图,大家希望找到离为主点离开方今的四个点。由于圆环伸张太快,外环做了诸多的无用扫描与排序。

图片 13

如此那般的用例很符合实际场景,早高峰车辆聚集在地铁周围,你从家出发一路向大巴,边走边找,共享单车软件上动态搜索距你如今的10辆车,附近只有三两辆,于是扩张寻找半径到大巴周围,将大巴周围的具有几千辆车都围观计算三遍,重临距离你近年来的任何的七八辆。

环境要求

爬取流程

接下去大家要求先物色得到用户详细音信和收获关怀列表的接口。

归来网页,打开浏览器的控制台,切换来Network监听情势。

咱俩先是要做的是寻觅一个大V,以轮子哥为例吧,它的个人新闻页面网址是:https://www.zhihu.com/people/excited-vczh

第一打开轮子哥的首页

图片 14

咱们得以见到那里就是她的一对基本音信,大家须求抓取的就是这几个,比如名字、签名、职业、关切数、赞同数等等。

接下去大家需求追究一下关注列表接口在什么地方,大家点击关怀选项卡,然后下拉,点击翻页,大家会在上面的乞求中发觉并发了
followees 初阶的 Ajax 请求。这些就是得到眷注列表的接口。

图片 15

我们着眼一下以此请求协会

图片 16

先是它是一个Get类型的请求,请求的URL是https://www.zhihu.com/api/v4/members/excited-vczh/followees,前边跟了五个参数,一个是include,一个是offset,一个是limit。

着眼后可以窥见,include
是一对取得关切的人的中坚消息的询问参数,包蕴回答数、小说数等等。

offset 是偏移量,大家今日分析的是第3 页的保护列表内容,offset 当前为40。

limit 为每一页的数额,那里是20,所以结合方面的 offset 可以估算,当
offset 为0 时,获取到的是第一页关切列表,当offset 为20
时,获取到的是第二页关怀列表,依次类推。

接下来接下去看下再次来到结果:

图片 17

可以寓目有 data 和 paging 三个字段,data
就是数码,包罗20个内容,这一个就是用户的主导音信,也就是关爱列表的用户消息。

paging里面又有多少个字段,is_end代表方今翻页是或不是得了,next
是下一页的链接,所以在判读分页的时候,大家可以先使用is_end看清翻页是还是不是甘休,然后再拿走
next 链接,请求下一页。

那样大家的关注列表就足以因此接口获取到了。

接下去大家再看下用户详情接口在哪儿,咱们将鼠标放到关怀列表任意一个头像上边,观察下网络请求,可以发现又会并发一个
Ajax 请求。

图片 18

可以见见本次的央求链接为https://www.zhihu.com/api/v4/members/lu-jun-ya-1
背后又一个参数include,include
是一对查询参数,与刚刚的接口类似,可是本次参数卓殊全,大概可以把装有详情获取下来,其余接口的末梢是加了用户的用户名,那一个实际是url_token,下面的不行接口其实也是,在回去数据中是可以收获的。

图片 19

于是综上所述:

理清了以上接口逻辑后,大家就足以起头布局请求了。

小结

因而上述的spider,大家完成了以上逻辑:

  • start_requests艺术,达成了第四个大V用户的详细音信请求还有她的粉丝和关切列表请求。

  • parse_user主意,达成了详细消息的领取和粉丝关心列表的收获。

  • paese_follows,达成了通过关心列表重新请求用户并拓展翻页的效益。

  • paese_followers,完成了通过粉丝列表重新请求用户并拓展翻页的机能。

Scrapy

Scrapy 是一个强有力的爬虫框架,安装格局如下:

pip3 install scrapy

Python3

本项目利用的 Python 版本是
Python3,项目上马从前请保管您早就设置了Python3。

MongoDB

非关系型数据库,项目开端此前请先安装好 MongoDB 并启动服务。

创办项目

安装好上述条件之后,我们便可以开端大家的花色了。
在品种上马之首先我们用命令行成立一个类型:

scrapy startproject zhihuuser

PyMongo

Python 的 MongoDB 连接库,安装格局如下:

pip3 install pymongo

成立爬虫

接下去我们须求创建一个
spider,同样利用命令行,然而本次命令行必要进入到项目里运行。

cd zhihuuser
scrapy genspider zhihu www.zhihu.com

变迁第一步请求

接下去大家要做的首先步当然是呼吁轮子哥的着力音信,然后拿走轮子哥的爱慕列表了,大家先是构造一个格式化的url,将部分可变参数提取出来,然后要求重写start_requests艺术,生成第一步的呼吁,接下去大家还须求按照取获得到关切列表做越来越的解析。

import json
from scrapy import Spider, Request
from zhihuuser.items import UserItem

class ZhihuSpider(Spider):
    name = "zhihu"
    allowed_domains = ["www.zhihu.com"]
    user_url = 'https://www.zhihu.com/api/v4/members/{user}?include={include}'
    follows_url = 'https://www.zhihu.com/api/v4/members/{user}/followees?include={include}&offset={offset}&limit={limit}'
    start_user = 'excited-vczh'
    user_query = 'locations,employments,gender,educations,business,voteup_count,thanked_Count,follower_count,following_count,cover_url,following_topic_count,following_question_count,following_favlists_count,following_columns_count,answer_count,articles_count,pins_count,question_count,commercial_question_count,favorite_count,favorited_count,logs_count,marked_answers_count,marked_answers_text,message_thread_token,account_status,is_active,is_force_renamed,is_bind_sina,sina_weibo_url,sina_weibo_name,show_sina_weibo,is_blocking,is_blocked,is_following,is_followed,mutual_followees_count,vote_to_count,vote_from_count,thank_to_count,thank_from_count,thanked_count,description,hosted_live_count,participated_live_count,allow_message,industry_category,org_name,org_homepage,badge[?(type=best_answerer)].topics'
    follows_query = 'data[*].answer_count,articles_count,gender,follower_count,is_followed,is_following,badge[?(type=best_answerer)].topics'

    def start_requests(self):
        yield Request(self.user_url.format(user=self.start_user, include=self.user_query), self.parse_user)
        yield Request(self.follows_url.format(user=self.start_user, include=self.follows_query, limit=20, offset=0),
                      self.parse_follows)

下一场大家兑现一下多个解析方法parse_userparse_follows

    def parse_user(self, response):
        print(response.text)
    def parse_follows(self, response):
        print(response.text)

最简易的达成他们的结果输出即可,然后运行观望结果。

scrapy crawl zhihu

此刻你会发觉出现了

401 HTTP status code is not handled or not allowed

访问被禁止了,那时大家着眼下浏览器请求,发现它比较从前的呼吁多了一个
OAuth 请求头。

图片 20

加入pipeline

在那边数据库存储使用MongoDB,所以在此地大家需求依靠Item
Pipeline,达成如下:

class MongoPipeline(object):
    collection_name = 'users'

    def __init__(self, mongo_uri, mongo_db):
        self.mongo_uri = mongo_uri
        self.mongo_db = mongo_db

    @classmethod
    def from_crawler(cls, crawler):
        return cls(
            mongo_uri=crawler.settings.get('MONGO_URI'),
            mongo_db=crawler.settings.get('MONGO_DATABASE')
        )

    def open_spider(self, spider):
        self.client = pymongo.MongoClient(self.mongo_uri)
        self.db = self.client[self.mongo_db]

    def close_spider(self, spider):
        self.client.close()

    def process_item(self, item, spider):
        self.db[self.collection_name].update({'url_token': item['url_token']}, {'$set': dict(item)}, True)
        return item

正如主要的某些就在于process_item,在此地运用了 update
方法,第三个参数传入查询条件,那里运用的是url_token,第四个参数传入字典类型的靶子,就是大家的
item,第两个参数传入True,那样就足以确保,要是查询数据存在的话就更新,不设有的话就插入。那样就足以确保去重了。

其余记得开启一下Item Pileline

ITEM_PIPELINES = {
    'zhihuuser.pipelines.MongoPipeline': 300,
}

接下来重新运行爬虫

scrapy crawl zhihu

诸如此类就可以窥见正常的出口了,会一贯不停地运转,用户也一个个被保存到数据库。

图片 21

看下MongoDB,里面大家爬取的用户详情结果。

图片 22

到今天完毕,整个爬虫就基本竣事了,大家最主要透过递归的措施落成了这几个逻辑。存储结果也经过适当的不二法门达成了去重。

思路分析

俺们都了然种种人都有关心列表和粉丝列表,尤其对于大V来说,粉丝和尊敬尤其更多。

设若大家从一个大V开首,首先可以博得她的个人消息,然后大家取得她的粉丝列表和关注列表,然后遍历列表中的每一个用户,进一步抓取每一个用户的音讯还有他们各自的粉丝列表和关心列表,然后再进一步遍历获取到的列表中的每一个用户,进一步抓取他们的新闻和关爱粉丝列表,循环往复,不断递归,那样就可以做到一爬百,百爬万,万爬百万,通过社交关系自然形成了一个爬取网,那样就足以爬到具备的用户音讯了。当然零粉丝零关注的用户就大意他们吧~

爬取的新闻怎么样来赢得呢?不用操心,通过分析今日头条的伏乞就足以获取有关接口,通过请求接口就可以得到用户详细音讯和粉丝、关注列表了。

接下去我们起头实战爬取。

欢迎大家关切腾讯云技术社区-乐乎官方主页,大家将四处在腾讯网为我们推荐技术精品小说哦~

更高成效

自然大家现在运行的是单机爬虫,只在一台微机上运行速度是个其他,所之前边大家要想提升抓取效能,须要用到分布式爬虫,在此间必要用到
Redis 来保证一个国有的爬取队列。

更加多的分布式爬虫的贯彻可以查阅祥和下手,丰衣足食!Python3网络爬虫实战案例

发表评论

电子邮件地址不会被公开。 必填项已用*标注