詳解簡書用戶信息采集——crawlspider&itemloader

爬取簡書全站用戶信息其實并不是特別容易,其中有個小坎就是A、B兩用戶互相關注,爬取時很容易造成死循環,這樣的死循環會越爬越多越爬越多,像雪球一樣越滾越大,造成爬取效率聚減,群內交流有說先入庫再去重其實是一樣的,當你入庫去重完畢后會發現入庫30W條,去重后只有5W條了,這都是由互相關注所造成的,下面給出策略圖、爬蟲代碼、代碼注釋,僅供參考。(翻到最后有驚喜)

策略圖

策略圖

一、策略講解

1、策略入口

1、策略入口

紅圈位置是策略圖入口,個人認為這些用戶都是大咖所以從這入手是再好不過的,里面有好幾頁AJX的用戶推薦名單,需要拼接和循環去獲取用戶KEY_ID,將用戶關注頁面分為2層,上層是用戶基本信息,下層是用戶關注信息,新建一個空集合,將爬取過基本信息用戶的KEY_ID存入集合,然后將下層關注信息遍歷取出其他用戶KEY_ID,然后讓去集合去判斷是否爬去過該KEY_ID,最后構筑這些用戶信息的關注頁面的鏈接做循環操作。

2、左擊關注

點擊查看全部之后推薦用戶的界面,點擊紅圈


2、左擊關注

3、取得用戶URL

紅圈中的字符串是 圖2 用戶的KEY_ID,爬取該用戶基本信息,將該KEY_ID放入集合(敲黑板!!!注意這里最好放集合,盡量不要用列表,用列表放入列表中的KEY_ID也都是重復的,不會去重,用集合可以節約開銷!),然后看url后面following是關注頁面的后綴,所以之后只需要將KEY_ID + following就可以構筑出關注頁面


3、取得用戶URL

4、取得關注頁面

這里需要注意紅圈的地方,如果關注用戶超過10個,有ajx動態加載的,需要去重新構筑URL遍歷用戶獲取關注的用戶KEY_ID
URL是這樣的 http://www.lxweimin.com/users/7e54016a5a06/following?page=1 自己去重構,不會看下面代碼,獲取完用戶KEY_ID然后用這些KEY_ID去集合中去重,如果不在集合里則拿出來構筑following關注頁面,就這樣再回到第一步。

4、取得關注頁面

二、代碼講解

Scrapy / python3.5

spider

# -*- coding: utf-8 -*-
import scrapy
import re
from urllib.parse import urljoin
from scrapy.http import Request
from article.items import JianShuItem, JianShuItemLoader


class JianshuSpider(scrapy.Spider):
    name = "jianshu"
    allowed_domains = ["www.lxweimin.com"]
    url_top_half = "http://www.lxweimin.com"
    start_urls = []
    used_id = set()
    # 設置一個空集合存爬過的用戶
    for pg in range(1, 11):
        # 第一步將推薦作者的鏈接放入start_url
        start_users_url_join = "http://www.lxweimin.com/recommendations/users?page=" + str(pg)
        start_urls.append(start_users_url_join)

    def parse(self, response):
        start_user_list = response.css("div.wrap a.avatar::attr(href)").extract()
        # 獲取推薦作者的href
        if start_user_list:
            # 起始頁面判斷,循環完start_url就沒用了
            # 判斷推薦作者href是否為空,空則爬完推薦作者,不進入if判斷
            for k_1 in start_user_list:
                # 取得href格式為"/users/"+key
                url_second_half1 = k_1 + "/following"
                # k_1是列表循環出來未經清洗的字符串格式為"/users/"+key 連接上"/following"
                user_following_url1 = urljoin(self.url_top_half, url_second_half1)
                yield Request(url=user_following_url1, callback=self.parse_kernel)
                # 核心解析函數

    def parse_kernel(self, response):
        # 既然解析item 又解析關注用戶
        reg_key = re.compile(r"http://www.lxweimin.com/users/(.*)/following")
        key_2 = reg_key.findall(str(response.url))[0]
        # 獲取key
        if key_2 not in self.used_id:
            # 判斷key是否使用過,阻斷互相關注循環
            item_loader = JianShuItemLoader(item=JianShuItem(), response=response)
            item_loader.add_css("key_id", "div.main-top a.name::attr(href)")
            item_loader.add_css("user_name", "div.main-top a.name::text")
            item_loader.add_css("contracted", ".main-top span.author-tag::text")
            item_loader.add_xpath("follow", ".//div[@class ='info']/ul/li[1]//p/text()")
            item_loader.add_xpath("fans", ".//div[@class ='info']/ul/li[2]//p/text()")
            item_loader.add_xpath("article", ".//div[@class ='info']/ul/li[3]//p/text()")
            item_loader.add_xpath("words_count", ".//div[@class ='info']/ul/li[4]//p/text()")
            item_loader.add_xpath("get_likes", ".//div[@class ='info']/ul/li[5]//p/text()")
            jianshu_item_loader = item_loader.load_item()
            self.used_id.add(key_2)
            # 將用過的key,放入集合
            yield jianshu_item_loader

        follow_num = response.xpath(".//div[@class ='main-top']//li[1]//p/text()").extract()[0]
        # 獲取該用戶關注人數
        follow_pg = round(int(follow_num)/9)
        # AJX每個頁面有9個關注
        if follow_pg != 0:
            # 關注數不為0的進入if判斷
            for f_pg in range(1, follow_pg+1):
                # 獲取該用戶關注人數的頁
                user_following_url_pg = response.url + "?page=" + str(f_pg)
                yield Request(url=user_following_url_pg, callback=self.parse_following)
                # 將每一頁的用戶關注人的url回調給parse_following

    def parse_following(self, response):
        following_list = response.css("ul.user-list a.avatar::attr(href)").extract()
        # 解析出被關注用戶的key
        for k_3 in following_list:
            key_3 = k_3.replace("/u/", "")
            # 清洗出key
            url_second_half3 = "/users/{}/following".format(key_3)
            user_following_url3 = urljoin(self.url_top_half, url_second_half3)
            # 直接拼接following url,返回給parse_kernel,進入循環
            yield Request(url=user_following_url3, callback=self.parse_kernel)

item

# -*- coding: utf-8 -*-
import re
import scrapy
from scrapy.loader import ItemLoader
from scrapy.loader.processors import MapCompose, TakeFirst, Join
from w3lib.html import remove_tags
from article.settings  import SQL_DATETIME_FORMAT, SQL_DATE_FORMAT
import datetime

# 以下為簡書用戶數據清洗函數
def key_id_filter(value):
    return value.replace("/u/", "")

def contracted_filter(value):
    if value == ' 簽約作者':
        return value.strip()
    elif value == "":
        return "未簽約"


class JianShuItemLoader(ItemLoader):
    default_output_processor = TakeFirst()
    # itemloader提取默認為list,所以這里需要重筑這個類的默認值


class JianShuItem(scrapy.Item):
    key_id = scrapy.Field(
        input_processor=MapCompose(key_id_filter)
    )
    user_name = scrapy.Field()
    contracted = scrapy.Field(
        input_processor=MapCompose(contracted_filter)
    )
    follow = scrapy.Field()
    fans = scrapy.Field()
    article = scrapy.Field()
    words_count = scrapy.Field()
    get_likes = scrapy.Field()

pipeline

# 常規寫法拿去改下參數就可以用了
class JianShuMongodbPipeline(object):
    def __init__(self):
        client = pymongo.MongoClient(
            host="localhost",
            port=27017,
        )
        db = client["jianshu"]
        self.coll = db["user_info"]

    def process_item(self, item, spider):
        self.coll.insert(dict(item))
        return item

以上是個人見解,如有錯誤或更優解歡迎交流~


最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,572評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,071評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,409評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,569評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,360評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,895評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,979評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,123評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,643評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,559評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,742評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,250評論 5 356
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,981評論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,363評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,622評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,354評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,707評論 2 370

推薦閱讀更多精彩內容

  • 源碼加翻譯 #import <Foundation/NSArray.h> #import <Foundation/...
    CAICAI0閱讀 1,162評論 0 50
  • 一. Java基礎部分.................................................
    wy_sure閱讀 3,823評論 0 11
  • width: 65%;border: 1px solid #ddd;outline: 1300px solid #...
    邵勝奧閱讀 4,850評論 0 1
  • iOS網絡架構討論梳理整理中。。。 其實如果沒有APIManager這一層是沒法使用delegate的,畢竟多個單...
    yhtang閱讀 5,224評論 1 23
  • 家鄉味其實就是,當你小的時候那種味蕾已深深的植入你的胃里,不一定別人喜歡,但你絕對執迷。其中油圪垛就是老家特有的產...
    3a87c8175f0a閱讀 7,795評論 21 24