静态网页爬虫实例1-简书搜索关键词制作词云

本文为《爬着学Python》系列第八篇文章。也是第一篇爬虫实例。旨在抛砖引玉先了解一下学习爬虫可以深入的有哪些知识。


结果

先上结果当我们谈论卡佛时我们在谈论什么-从简书文章说起

标题词云

代码

废话也先少说先上代码(请随意使用)

# -*- coding: utf-8 -*-
"""
Visual Studio Code
@date: Created on Sat Aug 19 09:32:29 2017
@author: SyPy
search instance:http://www.jianshu.com/search?q=
%E9%9B%B7%E8%92%99%E5%BE%B7%20%E5%8D%A1%E4%BD%9B&page=1&type=note
article instance:http://www.jianshu.com/p/7c917833c8bd
"""

import json
import jieba
import numpy as np
import requests as rqs
import PIL.Image as PIM
from bs4 import BeautifulSoup as BS
from wordcloud import WordCloud as WC


def get_links():
    """
    进行搜索并提取出文章链接信息,
    返回列表
    """
    def get_page():
        """
        获取各页搜索的请求地址,
        返回列表
        """
        return [SEARCH_URL + '&page=%s' %page for page in range(1, SEARCH_PAGES_COUNT + 1)]

    def get_response(search_url):
        """
        获取搜索页面结果的json数据,
        返回字典
        """
        return json.loads(rqs.get(search_url).content)

    def transfer_response(response_dict):
        """
        提取出将字典中和文章链接有关的信息,
        返回列表
        """
        return [DOMAIN_URL + '/' + entry['slug'] for entry in response_dict['entries']]

    def process_page(search_url):
        """
        对页内操作进行整合,
        返回列表
        """
        return transfer_response(get_response(search_url))

    return list(map(process_page, get_page()))

def flattern_urls(article_urls):
    """
    将多维列表拆开
    """
    for each in article_urls:
        if not isinstance(each, list) or isinstance(each, str):
            yield each
        else:
            yield from flattern_urls(each)

def get_articles(article_urls):
    """
    只要做词云,所以直接获取文章标题和内容保存,
    标题collect_title,
    正文collect_text
    """
    r_soups = []
    for article_url in article_urls:
        print(article_url)
        try:
            r_soup = BS(rqs.get(article_url, timeout=5).text, 'lxml')
        except:
            print('failed to open article site{}'.format(article_url))
            r_soup = BS("<h1 class='show-content'></h1>", 'lxml')
        r_soups += r_soup
    article_titles, article_texts = [[], []]
    for r_soup in r_soups:
        try:
            article_titles.append(str(r_soup.select('h1')[0].string))
            for paragraph in r_soup.select('.show-content')[0].select('p'):
                article_texts.append(str(paragraph))
        except AttributeError:
            continue

    return [article_titles, article_texts]

def process_articles(source):
    """
    删除标题和正文列表中的标签,
    内容简单,不用正则直接换即可
    """
    for article in source:
        article = str(article)
        article.replace('<div class="show-content" data-note-content="">', '')
        article.replace('</div>', '')
        article.replace('<p>', '')
        article.replace('</p>', '')
        article.replace('<h1 class="title">', '')
        article.replace('</h1>', '')

def produce_cloud(source, save_dir, process_cata):
    """
    制作文字云,保存在本地
    """
    source_text = ''.join(source)
    wordlist_jieba = jieba.cut(source_text, cut_all=True)
    wl_space_split = ' '.join(wordlist_jieba)

    img_bed = np.array(PIM.open(save_dir + 'imgbed.jpg'))
    gen_cloud = WC(background_color="white", max_words=2000,
                   mask=img_bed,
                   max_font_size=40, random_state=42,
                   font_path=save_dir + 'FZCSJW.TTF').generate(wl_space_split)
    gen_cloud.to_file(save_dir + process_cata + 'cloud.jpg')

if __name__ == '__main__':
    #可配置参数
    SEARCH_NAME = '%E9%9B%B7%E8%92%99%E5%BE%B7%20%E5%8D%A1%E4%BD%9B' #搜索关键字:雷蒙德 卡佛
    SEARCH_PAGES_COUNT = 30                                          #搜索页数
    LOCAL_DIR = 'G:\\action\\searchcarver\\'                           #文件本地保存地址
    #准备
    DOMAIN_URL = 'http://www.jianshu.com/p'
    SEARCH_URL = 'http://www.jianshu.com/search/do?q=' + SEARCH_NAME
    #获取链接
    ARTICLE_URLS_GROUP = get_links()
    ARTICLE_URLS = list(flattern_urls(ARTICLE_URLS_GROUP))
    print('{} links collected'.format(len(ARTICLE_URLS)))
    #获取文章标题和正文
    TITLE_LIST, TEXT_LIST = get_articles(ARTICLE_URLS)
    print('articles collected')
    print(TITLE_LIST[:20], type(TITLE_LIST[0]))
    #文本处理,生成文字云并保存
    process_articles(TITLE_LIST)
    process_articles(TEXT_LIST)
    produce_cloud(TITLE_LIST, LOCAL_DIR, 'title')
    produce_cloud(TEXT_LIST, LOCAL_DIR, 'text')

参数和函数说明

SEARCH_NAME是搜索的关键字,例如示例代码中的一长串就是中文‘雷蒙德 卡佛’,关于编码的相关知识会在以后进行说明,现在先给一个链接供不了解但好奇心较强的同学先解解渴The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!),作者是Stackoverflow的CEO,大佬的文章标题就是这么长不怪我。没什么兴趣的只需要直接在简书搜索你要的关键词,注意浏览器中地址的变化http://www.jianshu.com/search?q=%E9%9B%B7%E8%92%99%E5%BE%B7%20%E5%8D%A1%E4%BD%9B&page=1&type=note你复制粘贴就能得到这一长串乱码一样的东西了,事实上你在Python3的爬虫中requests请求地址有中文也是会自动编码的,也就是说http://www.jianshu.com/search?q=雷蒙德%20卡佛&page=1&type=note这样的地址并不妨碍你得到正常的响应。但我要说的是,如果是初学爬虫,要学会观察浏览器地址栏,这深入的话会涉及到web路由以及建站方案设计等,这就是为什么我们计划中会把Flask放进学习内容中,因为,仅仅是地址栏的信息是不够的,事实上我获取搜索结果的地址来自于浏览器的开发者工具,绝大多数浏览器F12即可快捷进入,我们需要通过其中的“Network”或者“网络”模块来查看网络请求信息,更多的应用或者程序中的网络请求我们还会用到“Fiddler”这样的抓包工具。看,只是一个地址,就有说不完的知识。计算机网络,这也是以后会介绍的内容。

SEARCH_PAGES_COUNT这个参数是搜索页数,因为我们手动查看可以看出来,搜索结果靠后的文章与搜索的关键字可能关系不大了。当然我们可以用语义分析去主动判断关联度,但是可以这个工作看到简书自己做得都很差,所以,这样的内容并不在本专题的计划内容范围内。当然,有兴趣的可以实现了以后往简书投简历:)

get_links是发出请求获取搜索结果,并且提取出具体文章的链接信息的函数。最后的返回值是一个包含几百个链接的列表。这样做其实是很笨重的,因为如果返回的数据量特别大,用列表存储显然就不明智了,我们理想的请求函数是一个生成器函数

其他函数也不多说了,代码中相应地方都用中文写了一些注释。它们不是今天要讲的重点。

正文

代码注释已经写得比较全了。尤其是请求搜索结果的函数,因为既然是第一次实例,主要讲一下请求数据思路。为了将步骤分离,展示数据变化的过程,写成了类似函数式编程的形式。后面会写成相对“正常”的形式。毕竟能把Python代码写成现在这么丑也是需要动一番脑子的。

我们认识爬虫,一般可以认为是用爬虫程序代替可视化浏览器进行网络访问,并且发挥程序的优点——客制化的需求,方便的处理数据、高效严谨的运行。

请求数据的思路

  • 需要什么样的数据,本例中是搜索结果各文章的链接
  • 发出请求,获取响应的数据
  • 分析返回数据的结构,清洗初始数据
  • 将清洗过的数据转化成下一步需要用到的链接

函数实现

像上面贴出来的代码中每一步都写清楚了做的事情。这里我们重新写一个一般形式的获取下载地址的函数。

def get_links():
    for page in range(1, 3):
        tmp_url = SEARCH_URL + '&page=%s' %page
        response = rqs.get(tmp_url)
        r_dict = json.loads(response.content)
        r_entries = r_dict['entries']
        for r_entry in r_entries:
            yield DOMAIN_URL + '/' + r_entry['slug']

这个重写的get_links函数可以直接替代进之前贴出来的代码完成相同的功能。从这两种形式我们也可以看到函数式编程非常优雅清晰,但是不如面向过程编程直白朴素。我们可以在这个函数中随意插入print函数来进行反馈,前者做不到的。

另外其中获取请求response的部分可以另写请求函数来满足各种需求(响应延迟,修改header,异常处理等)。甚至说我们可以创建下载器对象来使其更加健壮,将所有下载相关任务进行整合,但是这就属于比较有技巧的部分了,以后涉及到scrapy框架我们可以看到类似的想法是如何实现的,这里也不深究了。

总结

是的,我只贴了两段代码,并没有进行过多解释。一方面,单纯借代码尝试一下的复制粘贴改一两个参数就可以了,并不需要解释。另一方面有学习相关知识需求的,我建议还是踏踏实实慢慢学,像上面这种简单的爬虫说实话没有太大的实际意义,仅仅算是个小玩具而已。现在这个爬虫完全可以做成API部署在服务器上,需要改进和优化的地方数不胜数,对于具有实际意义来说还是任重道远。我们自然也可以借用爬虫熟悉Python编程,这也是我的初衷,不过这也要一步一步来不是吗。

链接

  1. The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)
  2. 爬虫 - 汪海的实验室 - CSDN博客
  3. amueller/word_cloud: A little word cloud generator in Python
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容