最近想用Python爬虫搞搞百度贴吧的操作,所以我得把原来申请的小号找出来用。有一个小号我忘了具体ID,只记得其中几个字母以及某个加入的贴吧。所以今天就用爬虫来获取C语言贴吧的所有成员。

计划很简单,爬百度贴吧的会员页面,把结果存到MySQL数据库中,等到所有会员都爬完之后。我就可以使用简单的SQL语句查询账号名了。由于C语言贴吧会员有50多万,所以我还需要在合适的时候(例如插入数据库失败)把错误信息打印到日志文件中。由于我是Python新手,所以就不弄什么多线程得了,直接一个脚本用到黑。

看着很简单,实际也很简单。写完了我看了一下,用到的知识只有最基础的SQL操作、BeautifulSoup解析。

首先第一步就是看一下这个吧的信息页有多少页,关键代码如下。踩了两天坑,总算感觉对BeautifulSoup熟悉了一点。代码也很简单,按照class名查找到总页数这个标签,然后用正则表达式匹配到页数数字。这里要说一下,正则表达式的分组真好用。以前偷懒只学了一点正则表达式,发现没啥作用,只有配合分组才能比较精确的查找字符。

html = request.urlopen(base_url).read().decode(encoding)

soup = BeautifulSoup(html, 'lxml')

page_span = soup.find('span', class_='tbui_total_page')

p = re.compile(r'共(\d+)页')

result = p.match(page_span.string)

global total_pages

total_pages = int(result.group(1))

logger.info(f'会员共{total_pages}页')

有了总页数,我们就可以遍历页面了,代码如下。写的虽然比较脏,但是能用就行了,大家嫌难看就难看吧。这里做的事情就很简单了,从第一页开始遍历,一直遍历到最后一页。把每一页的用户名字提取出来,然后用_insert_table(connection, name)函数存到MySQL中。

因为我为了省事,直接把百度用户名当做主键了。但是保不齐贴吧有什么bug,导致用户名重复之类的问题,导致插入失败。所以我用try把保存这一块包起来。有异常的话就打印到日志中,方便排查。日志分成两种级别的,INFO级别输出到控制台,ERROR级别输出到文件。

def _find_all_users():

global connection

for i in range(start_page, total_pages + 1):

target_url = f'{base_url}&pn={i}'

logger.info(f'正在分析第{i}页')

html = request.urlopen(target_url).read().decode(encoding)

soup = BeautifulSoup(html, 'lxml')

outer_div = soup.find('div', class_='forum_info_section member_wrap clearfix bawu-info')

inner_spans = outer_div.find_all('span', class_='member')

for index, span in enumerate(inner_spans):

name_link = span.find('a', class_='user_name')

name = name_link.string

logger.info(f'已找到 {name}')

try:

_insert_table(connection, name)

except:

logger.error(f'第{i}页{index}第个用户 {name} 发生异常')

完整的代码见下。

"""

Python写的百度贴吧工具

"""

import pymysql

host = 'localhost'

db_name = 'tieba'

username = 'root'

password = '12345678'

def _get_connection(host, username, password, db_name):

return pymysql.connect(host=host,

user=username,

password=password,

charset='utf8mb4',

db=db_name)

def _create_table(connection):

create_table_sql = """

CREATE TABLE tieba_member(

username CHAR(255) PRIMARY KEY

)

"""

with connection.cursor() as cursor:

cursor.execute(create_table_sql)

connection.commit()

def _insert_table(connection, username):

insert_table_sql = """

INSERT INTO tieba_member

VALUES(%s)"""

with connection.cursor() as cursor:

cursor.execute(insert_table_sql, (username,))

connection.commit()

import urllib.request as request

from bs4 import BeautifulSoup

import re

import tieba.log_config

import logging

logger = logging.getLogger()

encoding = 'GBK'

base_url = 'http://tieba.baidu.com/bawu2/platform/listMemberInfo?word=c%D3%EF%D1%D4'

# base_url = 'http://tieba.baidu.com/bawu2/platform/listMemberInfo?word=%B9%FD%C1%CB%BC%B4%CA%C7%BF%CD'

start_page = 1

total_pages = None

connection = _get_connection(host, username, password, db_name)

def _get_total_pages():

html = request.urlopen(base_url).read().decode(encoding)

soup = BeautifulSoup(html, 'lxml')

page_span = soup.find('span', class_='tbui_total_page')

p = re.compile(r'共(\d+)页')

result = p.match(page_span.string)

global total_pages

total_pages = int(result.group(1))

logger.info(f'会员共{total_pages}页')

def _find_all_users():

global connection

for i in range(start_page, total_pages + 1):

target_url = f'{base_url}&pn={i}'

logger.info(f'正在分析第{i}页')

html = request.urlopen(target_url).read().decode(encoding)

soup = BeautifulSoup(html, 'lxml')

outer_div = soup.find('div', class_='forum_info_section member_wrap clearfix bawu-info')

inner_spans = outer_div.find_all('span', class_='member')

for index, span in enumerate(inner_spans):

name_link = span.find('a', class_='user_name')

name = name_link.string

logger.info(f'已找到 {name}')

try:

_insert_table(connection, name)

except:

logger.error(f'第{i}页{index}第个用户 {name} 发生异常')

import datetime

if __name__ == '__main__':

_get_total_pages()

_find_all_users()

还有另一个文件用来配置日志的。你也可以把这两个文件合在一起,只不过看着可能更乱了。

import logging

# 创建Logger

logger = logging.getLogger()

logger.setLevel(logging.DEBUG)

# 创建Handler

# 终端Handler

consoleHandler = logging.StreamHandler()

consoleHandler.setLevel(logging.DEBUG)

# 文件Handler

fileHandler = logging.FileHandler('log.log', mode='a', encoding='UTF-8')

fileHandler.setLevel(logging.ERROR)

# Formatter

formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

consoleHandler.setFormatter(formatter)

fileHandler.setFormatter(formatter)

# 添加到Logger中

logger.addHandler(consoleHandler)

logger.addHandler(fileHandler)

性能测试

当然由于要爬的数据量比较大,我们还要计算一下可能的运行时间。首先不考虑爬虫被百度封了的情况。我把代码稍作修改,设定只爬前100页。

import datetime

if __name__ == '__main__':

# _get_total_pages()

total_pages = 100

time1 = datetime.datetime.today()

_find_all_users()

time2 = datetime.datetime.today()

print(time2)

print(time1)

print(time2 - time1)

结果如下,用时将近两分钟。做了简单计算得出结论,要爬完c语言贴吧的52万个会员,需要将近7个小时。所以程序还需要改进。

2017-04-04 23:57:59.197993

2017-04-04 23:56:10.064666

0:01:49.133327

首先先从数据库方面考虑一下。Windows下MySQL默认的数据库引擎是Innodb,特点是支持事务管理、外键、行级锁,但是相应的速度比较慢。我把表重新建为MyISAM类型的。然后重新运行一下测试,看看这次速度会不会有变化。

CREATE TABLE tieba_member (

username CHAR(255) PRIMARY KEY

)

ENGINE = MyISAM

这次性能提升的有点快,速度足足提高了76%。可见默认的并不一定是最好的。

2017-04-05 00:15:19.989766

2017-04-05 00:14:53.407476

0:00:26.582290

既然都开始测试了,不妨干脆点。MySQL还有一种引擎是Memory,直接把数据放到内存中。速度肯定会更快!不过测试结果很遗憾,还是26秒。可见数据库这方面的优化到头了。

CREATE TABLE tieba_member (

username CHAR(255) PRIMARY KEY

)

ENGINE = MEMORY

不过性能确实提高了很多。经过计算,这次只需要一个半小时即可爬完52万个用户。如果在开多个进程,相信速度还会更快。所以这篇文章就差不多完成了。等明天爬完之后,我把结果更新一下,任务就真正完成了!

不过结果很遗憾,爬虫失败了。为了速度更快我开了4个进程,分别爬1-5000页,5001-10000页,10001-15000页,以及15000-到最后4部分。

但是日志输出显示出现很多重复的用户名,5000页之后的用户名竟然和第一页相同。我百思不得其解,在使用浏览器测试发现,不知道是百度的防爬虫机制还是bug之类的,浏览器只能显示到450多页,在往后就会显示为空页面,如果页数更大,就一直返回第一页的内容。因此依赖于这个页面的贴吧爬虫宣布失败。

虽然失败了,但是还是学习到了不少经验。我测试了一下爬前450页,仅用时44秒。说明爬虫速度倒是还星还行。

import datetime

from multiprocessing import Process

if __name__ == '__main__':

total_pages = _get_total_pages()

processes = []

processes.append(Process(target=_find_all_users, args=(1, 150)))

processes.append(Process(target=_find_all_users, args=(151, 300)))

processes.append(Process(target=_find_all_users, args=(301, 450)))

time1 = datetime.datetime.today()

for process in processes:

process.start()

for process in processes:

process.join()

time2 = datetime.datetime.today()

print(f'开始时间{time1}')

print(f'结束时间{time2}')

print(f'用时{time2 - time1}')

python爬虫贴吧关注_Python 爬虫获取某贴吧所有成员用户名相关推荐

  1. python爬虫知乎点赞_Python爬虫爬取知乎小结

    最近学习了一点网络爬虫,并实现了使用Python来爬取知乎的一些功能,这里做一个小的总结.网络爬虫是指通过一定的规则自动的从网上抓取一些信息的程序或脚本.我们知道机器学习和数据挖掘等都是从大量的数据出 ...

  2. python通过ip池爬_python 爬虫 代理ip池(适合初学者)

    初次学习python爬虫的朋友在频繁访问被爬取页面网站时都会被拦截,也就是限制ip.这里教教大家建立代理ip池. #!/usr/bin/env python3# -*- coding: utf-8 - ...

  3. python爬虫模拟与思考_Python爬虫之模拟知乎登录

    昨天受邀在 CSDN 微信群做了一次 Python 技术分享,主题是<用Python模拟知乎登录>,效果非常不错,发现越来越多的人加入到了 Python 阵容中. 经常写爬虫的都知道,有些 ...

  4. python爬虫微博评论图片_python爬虫爬取微博评论

    原标题:python爬虫爬取微博评论 python爬虫是程序员们一定会掌握的知识,练习python爬虫时,很多人会选择爬取微博练手.python爬虫微博根据微博存在于不同媒介上,所爬取的难度有差异,无 ...

  5. python基础知识500题_python爬虫基础知识点整理

    更多编程教程请到:菜鸟教程 https://www.piaodoo.com/ 友情链接: 高州阳光论坛https://www.hnthzk.com/ 人人影视http://www.sfkyty.com ...

  6. 基于python爬虫的论文标题_python爬虫——简单论文标题检索-Go语言中文社区

    有趣的爬虫,独有的意义召唤着我去学习,去尝试.最近有感于每天对于论文的收集,感觉自己的收集速度赶不上论文的更新速度,同时对于自己想找到的论文的收集比较麻烦.因此,学习用python写一个很简单的爬虫, ...

  7. python通过ip池爬_Python爬虫 | IP池的使用

    一.简介 爬虫中为什么需要使用代理 一些网站会有相应的反爬虫措施,例如很多网站会检测某一段时间某个IP的访问次数,如果访问频率太快以至于看起来不像正常访客,它可能就会禁止这个IP的访问.所以我们需要设 ...

  8. python爬取收费素材_Python爬虫练习:爬取素材网站数据

    前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 在工作中的电子文案.ppt,生活中的新闻.广告,都离不开大量的素材,而素材 ...

  9. python爬虫抓取房产_Python爬虫实战(3):安居客房产经纪人信息采集

    1, 引言 Python开源网络爬虫项目启动之初,我们就把网络爬虫分成两类:即时爬虫和收割式网络爬虫.为了使用各种应用场景,该项目的整个网络爬虫产品线包含了四类产品,如下图所示: 本实战是上图中的&q ...

最新文章

  1. R语言计算回归模型每个样本(观察、observation、sample)的DFFITS度量实战:忽略单个观察(样本)时,回归模型所做的预测会发生多大的变化
  2. spring_了解Spring Web应用程序体系结构:经典方法
  3. 实战 | 深度学习轻松学:如何用可视化界面来部署深度学习模型 转载 2017年12月27日 00:00:00 109 翻译 | AI科技大本营 参与 | 王赫 上个月,我有幸结识了 DeepCogn
  4. 百练OJ:2714:求平均年龄
  5. python生成静态html_Python写静态HTML
  6. 六.dbms_session(提供了使用PL/SQL实现ALTER SESSION命令)
  7. service sshd start启动失败,Badly formatted port number.
  8. 数据预处理和数据特征工程
  9. Linux音频驱动-AOSC之Platform
  10. CAD转换PDF文件失败,打开后内容是空白的
  11. 谷歌浏览器开启JavaScript
  12. python读取文件夹中的所有图片并将图片名逐行写入txt中
  13. jquery动画顺序执行_深入理解jquery自定义动画animate()
  14. html transition属性,Transition属性详解
  15. Java基础知识面试题(2021最新版)
  16. 关键点检测评价指标OKS
  17. 网络推广能否为企业带来效益以及精彩回复
  18. 中国将强制统一充电接口,苹果如不低头,iPhone将被踢出中国市场
  19. 国际合作越来越多,如何国际化短视频源码(ios篇)
  20. 希捷酷鱼 7200.12 500GB (16MB Cache)硬盘亲身体验

热门文章

  1. 帮忙投票-2009年度全国儿童摄影展艺术展(第十期)江苏南京展区
  2. i春秋 WEB code
  3. linux gvim 配置文件,Gvim一些基本配置
  4. 十六进制表示法(进制转换)
  5. nutch1.3与solr3.4集成部署在eclipse上之——运行的输出日志
  6. np.ndarray.ravel()
  7. 【快速排序】n个元素最少要进行几趟?--王海艳
  8. 电视盒子怎么看启蒙动画片?HDP直播告诉你答案!
  9. SQL基础语法_赵俊杰
  10. 数据库复习----赵俊杰