太原百度公司

当前位置:首页 » 新闻动态 » 正文

太原百度公司
太原百度公司http://www.baidusx.cc

太原全网营销策划师讲解搜狗输入法词库爬取-搜狗词库抓取&解析

87 人参与  2019年08月22日 14:39  分类 : 新闻动态  点这评论

太原全网营销策划师讲解搜狗输入法词库爬取-搜狗词库抓取&解析

本次抓取的内容是:搜狗的全部词库

地址:pinyin.sogou.com/dict/c

这次有点复杂需要一些前置的知识:

  • 数据库表设计

  • 数据库的读取

  • 日志模块运用

  • 多线程的运用

  • 队列的运用

  • 字符串编码&解码

  • ...

项目文件梳理:

PS:这里的代码我是自己重构过,
公司里的代码要用内部框架来抓取。所以不能分享出来啦

吐个槽: 我司还在用Python2.7,编码的问题要把我搞死了!!
我猜是因为2.7的print 可以少打一对括号才一直不升级 逃~

来看一下项目结构:

├── configs.py  # 数据库的配置文件
├── jiebao.py    # 搜狗的词库是加密的,用来解密词库用的
├── spider
│   ├── log_SougouDownloader.log.20171118  # 词库下载日志
│   ├── log_SougouSpider.log.20171118           # 抓取日志
│   └── spider.py  # 爬虫文件
├── store_new
│   ├── __init__.py
│   └── stroe.py # 数据库操作的封装
└── utils
    └── tools.py # 日志模块

整体的还是比较清晰的
源文件我放在GitHub里:

github.com/Ehco1996/Pyt

爬虫逻辑&数据库表设计

来看一下入口的网页结构:

v2-081ac6df2adb0cea30d4ce9dedabe58c_r.jpg

我们需要解析 :

  • 一级分类

  • 二级分类

  • 文件名

  • 真实下载地址

由于数据量较大
有约1万个词库文件需要下载
有约 1亿 个关键词需要解析
不能将所有逻辑耦合在一起

这里我建立三张数据库表:

  • 存储词库二级分类入口的 cate 表:

v2-d5747bedcf0d27db1fe084e02691f16d_r.jpg

存储所有下载地址的 detail

v2-d5747bedcf0d27db1fe084e02691f16d_r.jpg

存储所有解析后关键词的keyword

v2-2fa443c972e8d088fbf926013541ced5_r.jpg

所以整体的逻辑是这样的:

  • 抓取二级分类的入口地址 写入cate 表

  • cate 表 读取地址发送请求后,解析下载地址存入detail表

  • detail 表 读取词库的下载地址下载词库到文件

  • 从本地读文件解析成关键词记录存入keyword 表

代码部分

首先是二级cate页的解析

   def cate_ext(self, html, type1):
        '''        解析列表页的所有分类名        Args:            html 文本            type1 一级目录名        
        '''
        res = []
        soup = BeautifulSoup(html, 'lxml')
        cate_list = soup.find('div', {'id': 'dict_cate_show'})
        lis = cate_list.find_all('a')
        for li in lis:
            type2 = li.text.replace('"', '')
            url = 'http://pinyin.sogou.com' + li['href'] + '/default/{}'
            res.append({
                'url': url,
                'type1': type1,
                'type2': type2,
            })
        return res

这里我通过解析一级分类的入口页来获取所有二级分类的地址

词库文件下载地址的解析:

   def list_ext(self, html, type1, type2):
        '''        解析搜狗词库的列表页面        args:            html: 文本            type1 一级目录名 
            type2 二级目录名 
        retrun list        每一条数据都为字典类型        '''
        res = []
        try:
            soup = BeautifulSoup(html, 'lxml')
            # 偶数部分
            divs = soup.find_all("div", class_='dict_detail_block')
            for data in divs:
                name = data.find('div', class_='detail_title').a.text
                url = data.find('div', class_='dict_dl_btn').a['href']
                res.append({'filename': type1 + '_' + type2 + '_' + name,
                            'type1': type1,
                            'type2': type2,
                            'url': url,
                            })
            # 奇数部分
            divs_odd = soup.find_all("div", class_='dict_detail_block odd')
            for data in divs_odd:
                name = data.find('div', class_='detail_title').a.text
                url = data.find('div', class_='dict_dl_btn').a['href']
                res.append({'filename': type1 + '_' + type2 + '_' + name,
                            'type1': type1,
                            'type2': type2,
                            'url': url,
                            })
        except:
            print('解析失败')
            return - 1
        return res

爬虫的入口:

   def start(self):
        '''        解析搜狗词库的下载地址和分类名称        '''
        # 从数据库读取二级分类的入口地址
        cate_list = self.store.find_all('sougou_cate')
        for cate in cate_list:
            type1 = cate['type1']
            type2 = cate['type2']
            for i in range(1, int(cate['page']) + 1):
                print('正在解析{}的第{}页'.format(type1 + type2, i))
                url = cate['url'].format(i)
                html = get_html_text(url)
                if html != -1:
                    res = self.list_ext(html, type1, type2)
                    self.log.info('正在解析页面 {}'.format(url))
                    for data in res:
                        self.store.save_one_data('sougou_detail', data)
                        self.log.info('正在存储数据{}'.format(data['filename']))
                time.sleep(3)

我这里通过从cate表里读取记录,来发请求并解析出下载地址

对于数据库操作的封装,可以直接阅读我 store_new/store.py这个文件
也可以看我上周写的文章:zhuanlan.zhihu.com/p/30

词库文件下载逻辑:

   def start(self):
        # 从数据库检索记录
        res = self.store.find_all('sougou_detail')
        self.log.warn('一共有{}条词库等待下载'.format(len(res)))
        for data in res:
            content = self.get_html_content(data['url'])
            filename = self.strip_wd(data['filename'])
            # 如果下载失败,我们等三秒再重试
            if content == -1:
                time.sleep(3)
                self.log.info('{}下载失败 正在重试'.format(filename))
                content = self.get_html_content(data[1])
            self.download_file(content, filename)
            self.log.info('正在下载文件{}'.format(filename))
            time.sleep(1)

这个部分没有什么难的,主要涉及到文件的读写操作

解析词库文件逻辑:

def start():
    # 使用多线程解析
    threads = list()
    # 读文件存入queue的线程
    threads.append(
        Thread(target=ext_to_queue))

    # 存数据库的线程
    for i in range(10):
        threads.append(
            Thread(target=save_to_db))
    for thread in threads:
        thread.start()

好了,所有的步骤都已经过了一遍
下面我们来说一些里面的「

Logging日志模块的运用

由于需要爬取和下载的数量比较大,
没有log是万万不行的,
毕竟我们不能时刻定在电脑面前看程序输出吧!
实际上,我们写的程序基本是跑在服务器上的。
我都是通过看程序的log文件来判断运行的状态。
在之前的代码里,可以看到我在很多地方都打了log
来看看日志文件都长啥样:

spider日志

v2-85af3d9c965125bad458b499378134be_hd.jpg

downloader日志

v2-f89a59b196de9272bbb5a93f9d8264dd_r.jpg

运行状态是不是一目了然了呢?
想知道怎么实现的可以看utils/tools.py这个文件
其实就是用了Python自带的logging模块

多线程的使用

词库文件下载下来一共9000+个
平均一个词库有2w条关键词
那么总共就是1.8亿条数据需要存储

假设存储一秒钟可以存储10条数据
那么这么多数据需要存:5000+小时
想想都可怕,如果真的要这么久,那黄花菜都凉了。

这里就需要我们用多线程来进行操作了,
由于涉及到文件的读写
我们得利用队列(queue)来帮助我们完成需求

逻辑是这样的:

首先开一条主线程对本地的词库文件进行读取/解析:

  • 从文件夹读取词库文件到内存

  • 解析词库文件,并将每个关键词存入队列之中

  • 周而复始~

其次开10~50条线程(取决于数据库的最大连接数)对队列进行操作:

  • 不停的从队列中取出一条记录,并存入数据库

  • 如果队列是空的,就sleep一会,等待主线程解析

  • 周而复始~


由于篇幅的长度,我只将从队列取数据的代码片段放出来
想要整个逻辑的可以去阅读jiebao.py

def save_to_db():
    '''    从数据队列里拿一条数据    并存入数据库    '''
    store = DbToMysql(configs.TEST_DB)
    while True:
        try:
            st = time.time()
            data = res_queue.get_nowait()
            t = int(time.time() - st)
            if t > 5:
                print("res_queue", t)
            save_data(data, store)
        except:
            print("queue is empty wait for a while")
            time.sleep(2)

利用多线程之后,速度一下快了几十倍
经过测试:1秒能存500条左右

最后来看一下数据库里解析的关键词:

QQ截图20190822144552.png

跑了一段时间之后,已经有4千万的数据啦

好了,这个项目就分享到这里,
还有很多细节部分得自己去看代码啦

山西百度公司联系方式

山西百度销售负责人:李经理

山西百度电话:15035183610

山西百度邮箱:490801481@qq.com

山西百度官网:http://www.baidusx.cc

山西百度公司地址:太原市高新技术开发区南中环街529号(南中环街与滨河东路交汇处东北角)太原清控创新基地B座3层、4层    

来源:山西百度公司(微信/QQ号:490801481),转载请保留出处和链接!

本文链接:http://www.baidusx.cc/post/291.html

太原百度公司http://www.baidusx.cc
太原百度公司http://www.baidusx.cc

本文标签:太原全网营销  太原全网策划  太原搜狗  太原搜狗输入法  搜狗词库  

微信公众号:QQ490801481 关注1元购买iPhone6!

加入【网络营销联盟】QQ:490801481(加群验证:山西百度公司)

<< 上一篇 下一篇 >>
太原百度公司http://www.baidusx.cc
太原百度公司http://www.baidusx.cc

相关文章

站长推荐

经验分享

网站分类

标签列表

太原百度公司http://www.baidusx.cc

    太原百度公司http://www.baidusx.cc

首页 | 网络推广 | 网络营销 | 百度信息流 | 百度品牌推广 | 电子商务| 站点地图

Copyright Your WebSite.Some Rights Reserved.