最近在自学python爬虫,写了一个小demo,前来现学现卖
感谢大神的博客!让我受益匪浅。python爬虫系列教程
开始之前啰嗦一句:不要因为我们的学习行为,影响到人家网站的正常运营
好了,开整!
首先博主用的是python3.6版本,据说python3的用户很少,我这也算个非主流了。。。
查看python版本,使用命令行直接敲入“python”命令即可
首先,来一小段代码开开胃:
import requests
class py:
def get_html(self, url):
html = requests.get(url)
print(html.text)
qiubai = py()
qiubai.get_html('http://www.qiushibaike.com/')
这里是使用了python的requests库进行http的get请求,requests库可以在命令行使用“pip install requests”安装。运行上述代码,会在控制台输出糗百首页的html源码,也是我们接下来工作的主要对象。
在google浏览器上按下F12可以调用开发者工具,看到网页的源码,而我们所关注的信息是在id为“content-left”的<div>标签下,每一个class为“article block untagged mb15”的<div>标签都是一个段子的信息,包括作者,内容,图片,好笑数,评论数,点赞数,点踩数等等。而class为“pagination”的<ul>标签则包含了页面跳转的链接。
我们的解析信息工作,从一个正则表达式开始(糗百会不定期改版,这条正则到时候可能就不好使了,小伙伴需要发挥自己的聪明才智了哦):
html = request.get(next_url, 3)
pattern = re.compile('<div class="author clearfix">.*?href.*?<img src.*?title=.*?<h2>(.*?)</h2>.*?'
'<div class="content">.*?<span>(.*?)</span>.*?</div>.*?</a>(.*?)<div class="stats">'
'.*?class="stats-vote"><i class="number">(.*?)</i>.*?<i class="number">(.*?)</i>.*?'
'hidden">(.*?)</span>.*?hidden">-(.*?)</span>', re.S)
# item[0] 用户昵称
# item[1] 发布内容
# item[2] 如果有内容,则为图片相关;否则是连续的\n符
# item[3] 好笑
# item[4] 评论数
# item[5] 赞
# item[6] 踩
items = re.findall(pattern, html.text)
# 匹配所有的<br/>字符
replaceBR = re.compile(r'<br/>')
# 匹配所有的\n串
replaceN = re.compile(r'(\n)+')
for item in items:
# 将item[2]中的连续的\n字符串替换成一个None字符
img = re.sub(replaceN, 'None', item[2])
if img == 'None':
print(item[0])
# 将item[1]中的所有的<br/>字符替换成换行符
print(re.sub(replaceBR, '\n', str(item[1])))
print(u'好笑:', item[3], u' 评论:', item[4], u' 赞:', item[5], u'踩:', item[6])
所以匹配到的字符都会加入到一个我们定义的名为items的数组中,这个数组的长度最小为0(没有匹配),数组的每个元素又是一个长度为7的数组(因为我们在正则表达式中共有7个"(.*?)")。
乍一看这个正则表达式,有的人可能会有点懵逼,我们只看它的一小部分,“窥一斑而知全豹”,听贫道慢慢道来。
每个段子包含的第一部分内容,就是作者的一些个人信息,<img>标签的链接是作者头像,<h2>标签中的内容是作者的昵称,再往下的<div>标签显示了作者的性别和年龄。我们的正则表达式匹配的就是以“author clearfix">”开头的,后续任意字符(也就是“.*?”,匹配任意长度任意字符),然后匹配“href”字符等等,以此类推,直到“<h2>(.*?)</h2>”这里我们注意一下,在上图中我们可以看到,它匹配的是“<h2>Boltzmann</h2>”这段,“(.*?)”匹配的也就是用户的昵称。
正则表达式是个好东西,不过博主是不怎么懂的,想学习的小伙伴可以自行查找资料,学有所成了记得来教教博主啊!感激不尽!
通过上述的代码我们可以获取当前页的所有段子,但是既然说是爬虫,我们的小虫子得主动出击啊,爬取一页之后应该去下一个页面继续工作。当然不是所有的网站都有类似“下一页”、“更多”这样的按钮的,那样的网站如何爬取遇到再说;幸运的是,糗百有啊,那我们还等什么,整!
上文说过,页面跳转的链接都在class为"pagination"的<ul>标签中,每个<li>标签都是一个地址链接,我们需要做的工作就是:
1. 找到这个<ul>标签
2. 找到<ul>标签下所有的<li>标签
3. 从<li>标签中找到下一个页面的地址链接
这里给大家介绍一个简单粗暴的python库,叫 BeautifulSoup,使用这个库推荐安装lxml,安装命令当然是“pip install lxml”。
上代码:
# 获取class为pagination的ul标签,获取其中的页面跳转相关内容
pagination = BeautifulSoup(html.text, 'lxml').find('ul', class_='pagination')
# 获取pagination中所有的li标签,大多数最后一个标签都包含下一个页面的地址
li = BeautifulSoup(str(pagination), 'lxml').find_all('li')
next_pattern = re.compile('<a href="(.*?)" rel', re.S)
# 检测最后一个li标签是否含有地址信息
next = re.findall(next_pattern, str(li[-1]))
# print(li)
# 当下个页面链接不为空时,检测数据库中是否已经存在连接
if next:
print('the next page is:', next[-1])
# 如果地址在库中存在,终止操作,否则拼接好url,继续执行后续操作
if url_queue.find_by_id(next[-1]):
break;
# 地址信息入库
info = {
'_id': next[-1],
'timestamp': datetime.now()
}
url_queue.push(info)
next_url = base_url + next[-1]
# 当下个页面链接为空时,退出循环
else:
print(r'I don\'t konw where to go : ', next)
break
细心的小伙伴发现了这里使用了数 据 库的操作,我这里用的是简单的插入和查询操作判断地址是否爬取过,逻辑处理得也很是草率,希望小伙伴们在自己使用的过程中加以改良,奉上mongodb操作的代码:
from pymongo import MongoClient, errors
class MongoQueue():
# 初始化mongodb连接
def __init__(self, db, collection, timeout=300):
self.client = MongoClient()
self.Client = self.client[db]
self.db = self.Client[collection]
self.timeout = timeout
# 将数据插入数据库
def push(self, info):
try:
self.db.insert(info)
print('插入成功')
except errors.DuplicateKeyError as e:
print('插入失败')
print(e)
pass
# 根据id查找数据库中的记录
def find_by_id(self, id):
recode = self.db.find_one(
{
'_id': id
}
)
return recode
def clear(self):
self.db.drop()
代码已上传csdn资源,大家可以免费下载,点击打开链接 欢迎大家拍砖~~~
最后再叮嘱一句:不要影响到人家网站的正常运营!