抓取百万级简书用户做数据挖掘

简书日活用户至少几十万,因此抓取简书的用户,一方面做数据分析和挖掘,另一方面,看看有哪些it同行,挖掘一些高质量的文章。

本文先分析抓取数据,结合抓取策略和scrapy框架,一步一步教你带你做爬虫和挖掘分析。

先说抓取思路。本文思路是选择一个大V,大v就是关注用户较多,粉丝也多的用户作为爬虫抓取入口,然后不断对其关注用户和粉丝用户抓取,再对关注用户和粉丝的关注用户和粉丝进行抓取,如此循环。

抓取函数入口:

    def start_requests(self):
        start_url = 'https://www.jianshu.com/u/811ae6268caa'
        yield Request(start_url, callback=self.parse)
parse函数式scrapy的回调函数,主要解析用户信息和用户发表的文章,同时解析出其关注列表和粉丝列表进行递归抓取。当然抓取列表显然是有多页的,而这在简书里面的体现在ajax异步加载,只在拖动下拉
的时候才会触发请求数据。这里拿到分页列表后,采取url拼接的方式构造分页请求。
followed_url = 'https://www.jianshu.com'+info_selectors[0].xpath("./div/a/@href").extract()[0]
        followed = info_selectors[0].xpath("./div/a/p/text()").extract()[0]
        pages = int(float(followed)/10)
        for page in range(1,pages+1):
            userlist_url = followed_url + '?page={page}'.format(page=page)
            yield Request(userlist_url, callback=self.parseuserlist, dont_filter=True)

同理抓取粉丝列表。抓取用户文章也一样。

 articles_url = 'https://www.jianshu.com' + info_selectors[2].xpath("./div/a/@href").extract()[0]
        articles = info_selectors[2].xpath("./div/a/p/text()").extract()[0]
        #抓取文章
        article_page= int(float(articles)/10)
        for page in range(1,article_page+1):
            articellist_url = articles_url + '?order_by=shared_at&page={page}'.format(page=page)
            yield Request(articellist_url, callback=self.parse_article_list, dont_filter=True)

这样既可抓取文章了。

 再看parseuserlist和parse_article_list。解析用户信息和文章信息,然后持久化到文件。

    def parseuserlist(self,response):
        url_list = response.xpath("//ul[@class='user-list']/li/div[@class='info']/a/@href").extract()
        url_list = ['https://www.jianshu.com'+url for url in url_list]
        for url in url_list:
            yield Request(url,callback=self.parse,dont_filter=True)
    def parse_article_list(self, response):
        """
        根据总文章数抓取的每一页的文章列表
        :param response:
        """
        base_url_p = 'https://www.jianshu.com/p/'
        all_links_p = [base_url_p + link for link in re.compile('href="/p/(.*?)"', re.S).findall(response.text)]
        all_article_links = set(all_links_p)
        for article_link in all_article_links:
            yield Request(url=article_link, callback=self.parse_article, dont_filter=True)

抓取文章列表的时候对每篇文章进行解析,parse_article函数负责解析。

    def parse_article(self, response):
        try:
            bs = BeautifulSoup(response.text, 'lxml')
            url = response.url
            article = ArticleItem()
            article['id'] = str(url).split('/')[-1].split('?')[0]
            article['url'] = url
            article['title']=bs.select("title")[0].get_text()     # 标题
            article['name']=bs.select("div[class='author']")[0].select("span[class='name']")[0].get_text() # 作者名字
            article['post_time']=bs.select("span[class='publish-time']")[0].get_text()  # 发布时间
            article['content']=bs.select("div[class='show-content']")[0].get_text()     # 文章内容
            # article['read_count']= bs.select("span[class='views-count']")[0].get_text() if bs.select("span[class='publish-time']") else "0" # 阅读次数
            # article['comment_count']=bs.select("span[class='comments-count']")[0].get_text() if bs.select("span[class='comments-count']") else "0" # 评论次数
            # article['love_count']=bs.select("span[class='likes-count']")[0].get_text() if bs.select("span[class='likes-count']") else "0"       # 喜欢次数
            yield article
        except Exception as e:
            print(e)

持久化pipeline,这里为了简便直接写入文件。

    def process_item(self, item, spider):
        if isinstance(item, JianshuItem):
            with open('jianshu_user.csv', 'a+', encoding='utf-8', newline='') as f:
                writer = csv.writer(f)
                writer.writerow((item['id'], item['url'], item['nickname'], item['description'], item['followed'], item['following'], item['articles'], item['charlength'], item['likes']))
                return item
        elif isinstance(item, ArticleItem):
            with open('jianshu_article.csv', 'a+', encoding='utf-8', newline='') as f:
                writer1 = csv.writer(f)
                writer1.writerow(
                    (item['id'], item['url'], item['name'], item['title'], item['post_time'], item['content']))
            return item

这里要保存的数据分为user和article两个类。保存的数据在item类体现。

class JianshuItem(Item):
    url = Field() #主页
    id = Field()
    nickname = Field()
    description = Field()
    followed = Field()
    following = Field()
    articles = Field()
    charlength = Field()
    likes = Field()

class ArticleItem(Item):
    url = Field()   #当前网页的url
    id = Field()    #作者ID
    name = Field()  # 作者名字
    title = Field() #标题
    post_time = Field()    #发布时间
    content = Field()      #文章内容
    read_count = Field()   #阅读次数
    comment_count = Field()#评论次数
    love_count = Field()   #喜欢次数

最后,为了反爬虫,增加模拟浏览器的请求头,在scrapy的增加HeadersDownloaderMiddleware中间件,并在配置文件开启。

class HeadersDownloaderMiddleware(UserAgentMiddleware):
    
    def process_request(self, request, spider):
        ua = random.choice(UserAgentList)
        request.headers.setdefault('User-Agent:', ua)

其中

UserAgentList为手动配置的,当然也可以使用fake—Agent。
最后,在命令行执行:
sys.path.append(os.path.dirname(os.path.basename(__file__)))
execute(['scrapy','crawl','jianshu'])

爬虫就跑起来了。

 
原文地址:https://www.cnblogs.com/hd-zg/p/9106968.html