一步一步学习小爬虫

通过一个例子一步一步学习 Python 爬虫。

  • img/@src 提取 img 元素中属性名为 src 的对应的 属性值
  • 通过 Chrome 的审查元素得到的 Xpath 路径和网页源代码中的路径有所不同
  • post 请求中的可变字段一般先通过登录前的一次 get 请求获取

比较疑惑的一个地方是, 使用 Chrome 右键菜单中的 检查, 所找到的 tbody 元素在网页源文件中并不存在。把 tbody 去掉也没有影响。于是决定不使用 tbody 标签了。

1
//tbody/tr/td[@id="main"]/div[@id="content"]/div[@id="stream"]/ol[@class="wa solo"]/li

完整代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# -*-coding: utf-8 -*-
import requests
from lxml import etree

class FanFou(object):
def getDataForm(self):
base_url = "http://fanfou.com"
token = etree.HTML(requests.get(base_url).text).xpath('//form/p[@class="act"]/input[@name="token"]/@value')[0]
dataform = {
'loginname' : 'qiangyuebing@ali.com',
'loginpass' : 'sanxingnote7',
'action' : 'login',
'token' : token
}
return dataform

def getInfo(self, data):
login_url = "http://fanfou.com/login"
html = requests.post(login_url, data = data).content
# messages = etree.HTML(html).xpath('//tbody/tr/td[@id="main"]/div[@id="content"]/div[@id="stream"]/ol[@class="wa solo"]/li')
messages = etree.HTML(html).xpath('//ol[@class="wa solo"]/li')
info_List = []

for li in messages:
info = {}
info['home_url'] = li.xpath('a/@href')[0] # 个人主页
info['title'] = li.xpath('a[@class="author"]/text()')[0] # 姓名
info['avatar'] = li.xpath('a[@class="avatar"]/img/@src')[0] # 头像地址
info['content'] = li.xpath('span[@class="content"]')[0].xpath('string(.)') # 发言
info['message_time'] = li.xpath('span[@class="stamp"]/a[@class="time"]/text()')[0] # 发言时间
info['app'] = li.xpath('span[@class="stamp"]/span[@class="method"]')[0].xpath('string(.)') # 使用的客户端
info_List.append(info)
return info_List

def savetofile(self, infoList):
base_url = 'http://fanfou.com'
fout = open("result.txt", "a", encoding="utf-8")
for info in infoList:
fout.writelines(u'个人主页: ' + base_url + info['home_url'] + "\n")
fout.writelines(u'姓名: ' + info['title'] + "\n")
fout.writelines(u'头像地址: ' + info['avatar'] + "\n")
fout.writelines(u'发言内容: ' + info['content'] + "\n")
fout.writelines(u'发言时间: ' + info['message_time'] + "\n")
fout.writelines(u'客户端: ' + info['app'] + "\n")
fout.writelines("\n\n")
fout.close()

if __name__ == '__main__':
myfan = FanFou()
dataform = myfan.getDataForm()
infoDict = myfan.getInfo(dataform)
myfan.savetofile(infoDict)
print('download complete')

结果如下:(目前只写入 txt, 随后的文章中会使用数据库)

注意, 例子中的用户名和密码不是真实的, 请使用自己的信息进行替换。

result

饭否中的帖子不是一次性加载完的, 也没有分页, 当点击 更多的时候才会加载更多内容, 所以用到了 Ajax 异步加载, 后面的文章会使用这个功能。