昨晚使用不熟悉的xpath语法解析百度新闻页面碰到了好多坑,今天继续通过简单的豆瓣图书进行练习
1.分析页面
进入豆瓣首页https://www.douban.com/
在第一行导航栏找到读书:https://book.douban.com/
进入页面之后发现有很多内容,由于豆瓣没有反爬措施,所以多抓取些数据
大致浏览后发现应该能通过标签查找到全部图书,找到所有热门标签
点击所有热门标签https://book.douban.com/tag/?view=cloud
浏览页面后大致可以确定这个入口是合适的一个入口
2.分析入口页面
打开浏览器自带的开发者模式找到其中一个标签:小说
1<td><a href="/tag/小说">小说</a>
点击小说:https://book.douban.com/tag/小说
发现它的url就是域名https://book.douban.com/
和/tag/小说
的组合
3.分析tag页面
进入小说
页面之后发现一本书大概分为8个关键部分:封面
,书名
,作者
,出版社
,日期
,售价
,评分
,简介
1<img class="" src="https://img3.doubanio.com/mpic/s27264181.jpg" width="90">
2
3<ahref="https://book.douban.com/subject/25862578/"
title="解忧杂货店"onclick="...">解忧杂货店</a>
4
5<div class="pub">[日] 东野圭吾 / 李盈春 / 南海出版公司 / 2014-5 / 39.50元</div>
6
7<span class="rating_nums">8.6</span>
8
9<p>现代人内心流失的东西,这家杂货店能帮你找回——僻静的街道旁有一家杂货店,
10只要写下烦恼投进卷帘门的投信口,第二天就会在店后的牛奶箱里得到回答。
11因男友身患绝... </p>
4.开始
首先导入需要的库
1from lxml import etree
2import requests
这次使用类的方式实现
1class DouBanBook():
2 pass
初始化类:
1def __init__(self):
2 self.url_book = 'https://book.douban.com/tag/?view=cloud'
3 self.page = 0
获取tag列表
1def get_tags(self):
2 '''
3 获取tag列表
4 '''
5 tags = [] #创建一个空列表存储tag名
6 result = requests.get(self.url_book).text
7 #开始xpath解析
8 html = etree.HTML(result)
9 tags_list = html.xpath
('//[@id="content"]/div/div[1]/div[2]/div/table/tbody/tr/td/a')
10 for tag in tags_list:
11 tags.append(''.join(tag.xpath('./text()')))
12 return tags
由于数据比较大且有分析价值,对数据进行持久化操作(存入数据库)
首先新建如下库和表
1def insert_mysql(self,item):
2 #连接数据库
3 db = pymysql.connect(host='127.0.0.1', port=3306, user='root', password='123456', db='douban',charset="utf8")
4 #写sql语句
5 sql = "INSERT INTO douban_book
6(tag,pic,titiel,author,press,price,time,score,book_info)
7VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s);"
8 #尝试插入数据
9 try:
10 with db.cursor() as cursor:#创建游标
11 cursor.execute(sql, (item['tag'],item['book_pic'],item['book_titiel'],item['book_author']
12,item['book_press'],item['book_price'],item['book_time'],
13item['book_score'],item['book_info']))
14 db.commit()#提交数据
15 except Exception as e:#如果出现异常插入11书名111111
16 cursor.execute(sql,('1','1',item['book_titiel'],'1','1','1','1','1','1',))
17 db.commit()
将八项重要内容写入数据库
1def get_book_message(self,tag):
2 '''
3 将八项重要信息存储到mysql数据库
4 '''
5 #拼接tag和page成新的url
6 url = 'https://book.douban.com/tag/'+tag+'?start=%d&type=T' %self.page
7 result = requests.get(url).text
8 #开始xpath解析
9 html = etree.HTML(result)
10 books_list = html.xpath('//*[@id="subject_list"]/ul/li')
11 #由于翻页操作是通过回调函数实现,所以需要做跳出循环的判断
12 if books_list == []:
13 return
14 item = {}#新建一个空字典存储获取到的信息
15 item['tag'] = tag
16 for book in books_list:
17 #封面
18 item['book_pic'] = ''.join(book.xpath('./div[1]/a/img/@src'))
19 #书名
20 item['book_titiel'] = ''.join(book.xpath('./div[2]/h2/a/@title'))
21
22 _book_press = book.xpath('./div[2]/div[1]/text()')[0].split('\n')
23 #由于作者,出版社,时间,售价是通过/分割的所以写一个列表推倒式存储
24 book_press = ''.join([book_press for book_press in _book_press if '/' in book_press])
25 #作者/出版社/时间/售价
26 #查看多条数据后发现最后一条必为价格,最后第二条为时间,最后第三条为出版社
27 item['book_price'] = book_press.split('/')[-1]
28 item['book_time'] = book_press.split('/')[-2]
29 item['book_press'] = book_press.split('/')[-3]
30 item['book_author'] = ''.join(book_press.split('/')[:-3])
31 #评分
32 item['book_score'] = ''.join(book.xpath('./div[2]/div[2]/span[2]/text()'))
33 #简介
34 item['book_info'] = ''.join(''.join(book.xpath('./div[2]/p/text()')).split('\n'))
35 self.insert_mysql(item)#写入数据库
36 #print(item)
37 self.page += 20
38 self.get_book_message(tag)
整个类:
1 class DouBanBook():
2 def __init__(self):
3 self.url_book = 'https://book.douban.com/tag/?view=cloud'
4 self.page = 0
5 def get_tags(self):
6 '''
7 获取tag列表
8 '''
9 tags = [] #创建一个空列表存储tag名
10 result = requests.get(self.url_book).text
11 #开始xpath解析
12 html = etree.HTML(result)
13 tags_list = html.xpath('//[@id="content"]/div/div[1]/div[2]/div/table/tbody/tr/td/a')
14 for tag in tags_list:
15 tags.append(''.join(tag.xpath('./text()')))
16 return tags
17
18 def get_book_message(self,tag):
19 '''
20 将八项重要信息存储到mysql数据库
21 '''
22 #拼接tag和page成新的url
23 url = 'https://book.douban.com/tag/'+tag+'?start=%d&type=T' %self.page
24 result = requests.get(url).text
25 #开始xpath解析
26 html = etree.HTML(result)
27 books_list = html.xpath('//*[@id="subject_list"]/ul/li')
28 #由于翻页操作是通过回调函数实现,所以需要做跳出循环的判断
29 if books_list == []:
30 return
31 item = {}#新建一个空字典存储获取到的信息
32 item['tag'] = tag
33 for book in books_list:
34 #封面
35 item['book_pic'] = ''.join(book.xpath('./div[1]/a/img/@src'))
36 #书名
37 item['book_titiel'] = ''.join(book.xpath('./div[2]/h2/a/@title'))
38
39 _book_press = book.xpath('./div[2]/div[1]/text()')[0].split('\n')
40 #由于作者,出版社,时间,售价是通过/分割的所以写一个列表推倒式存储
41 book_press = ''.join([book_press for book_press in _book_press if '/' in book_press])
42 #作者/出版社/时间/售价
43 #查看多条数据后发现最后一条必为价格,最后第二条为时间,最后第三条为出版社
44 item['book_price'] = book_press.split('/')[-1]
45 item['book_time'] = book_press.split('/')[-2]
46 item['book_press'] = book_press.split('/')[-3]
47 item['book_author'] = ''.join(book_press.split('/')[:-3])
48 #评分
49 item['book_score'] = ''.join(book.xpath('./div[2]/div[2]/span[2]/text()'))
50 #简介
51 item['book_info'] = ''.join(''.join(book.xpath('./div[2]/p/text()')).split('\n'))
52 self.insert_mysql(item)#写入数据库
53 #print(item)
54 self.page += 20
55 self.get_book_message(tag)
56
57 def insert_mysql(self,item):
58 #连接数据库
59 db = pymysql.connect(host='127.0.0.1', port=3306, user='root', password='123456', db='douban',charset="utf8")
60 #写sql语句
61 sql = "INSERT INTO douban_book
62(tag,pic,titiel,author,press,price,time,score,book_info)
63VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s);"
64 #尝试插入数据
65 try:
66 with db.cursor() as cursor:#创建游标
67 cursor.execute(sql, (item['tag'],item['book_pic'],item['book_titiel'],item['book_author']
68,item['book_press'],item['book_price'],item['book_time'],
69item['book_score'],item['book_info']))
70 db.commit()#提交数据
71 except Exception as e:#如果出现异常插入11书名111111
72 cursor.execute(sql,('1','1',item['book_titiel'],'1','1','1','1','1','1',))
73 db.commit()
开始调用:
由于全部图书有点多,使用线程池加快点速度并记录下耗时
耗时1021s,相当于17分钟速度还有待提升
1if __name__ == '__main__':
2 starttime = time.time()
3 book = DouBanBook()
4 p = Pool()
5 for tag in book.get_tags():
6 p.apply_async(book.get_book_message,args=(tag,))
7
8 p.close()
9 p.join()
10 usetime = time.time()-starttime
11 print('总耗时%s'%usetime)
综上总共导入的模块如下:
1from multiprocessing import Pool #进程池
2from lxml import etree #lxml解析页面
3import pymysql #连接mysql数据库了
4import requests #模拟http/https访问
5import time #时间模块