【干货】一个小项目的运用及解析日志日志

优采云 发布时间: 2021-08-27 19:14

  【干货】一个小项目的运用及解析日志日志

  不知不觉中,我已经上课三个星期了,还写了几个小项目。这里有一个有趣的爬虫项目:搜狗输入法词库爬虫,和你聊聊公司这里是如何写项目指南

  本次抓取的内容为:搜狗所有词库

  地址:/dict/cate/index/167

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

  项目文件整理:

  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 中:

  /Ehco1996/Python-crawler/tree/master/搜狗

  爬虫逻辑和数据库表设计

  看门户的网页结构:

  

  我们需要解决:

  由于数据量大

  大约有 10,000 个同义词库文件可供下载

  大约有1亿关键词需要解决

  无法将所有逻辑耦合在一起

  这里我创建了三个数据库表:

  

  

  

  所以总体逻辑是这样的:

  代码部分

  一、二级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文件

  你也可以阅读我上周写的文章:/p/30911268

  词库文件下载逻辑:

   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 日志模块的使用

  因为爬取下载次数比较多,

  没有日志是绝对不可能的,

  毕竟我们不能总是在电脑前看程序输出!

  其实我们写的程序基本上都是在服务器上运行的。

  我总是通过查看程序的日志文件来判断运行状态。

  在前面的代码中,可以看到我登录了很多地方

  让我们看看日志文件是什么样的:

  蜘蛛日志

  

  下载器日志

  

  运行状态一目了然?

  如果你想知道如何实现它,你可以看到文件utils/tools.py

  其实就是使用Python自带的日志模块

  多线程的使用

  共下载 9000 多个同义词库文件

  字典关键词平均有2w个词条

  那么总共需要存储1.80亿条数据

  假设一秒可以存储10条数据

  需要存储这么多数据:5000 多个小时

  想想就很可怕。真的要这么久,黄花菜会冷的。

  这里需要使用多线程来操作,

  因为涉及文件读写

  我们必须使用队列来帮助我们满足需求

  逻辑是这样的:

  首先打开一个主线程来读取/解析本地词库文件:

  接下来打开10~50个线程(取决于数据库的最大连接数)来操作队列:

  由于篇幅原因,我只放出从队列中取数据的代码片段

  如果你想要整个逻辑,你可以阅读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条记录

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

  

  运行一段时间后,已经有4000万条数据了。

0 个评论

要回复文章请先登录注册


官方客服QQ群

微信人工客服

QQ人工客服


线