Python爬取豆瓣图书信息学习记录
来源:互联网 发布:c语言 购物款 编辑:程序博客网 时间:2024/04/29 03:02
Python爬取豆瓣网图书信息
(一)爬虫思路:
# 爬虫思路汇总:# ①,https://book.douban.com/tag/ 总书签首页# 抓取豆瓣图书书签上所有的书签名字,并保存为一个数组# 当输入一个标签时,根据标签去生成对应的网址。如果标签不存在数组中,提示帮助,然后显示这个标签数组内容# ②,多线程爬取豆瓣图书信息# 1,爬取图书名字跟作者# 2,爬取图书对应的链接# 3,爬取图书的简介信息# 4,爬取图书的豆瓣评分# ③,将数据储存在xls表格中,按标签分类命名xls文件。# 按一定条件排序:评分或者默认排序# 项目目的:# ①,熟悉xpath提取与正则 ②,熟悉threading多线程# ③,熟悉表格输出操作openpyxl模块操作# ④,熟悉文件存取操作
本次代码通过自主学习 openpyxl 与threading模块,完成了后面部分的要求,爬取了对应的 书名—作者—豆瓣评分—参与评分总人数—地址链接,这里没有对简介信息做操作,考虑到文字太多表格存储不方便。所以就没弄了,影响不大。
(二)源代码讲解:
Ⅰ. 总标签分类的获取
- 由于时间问题,这里就直接给贴上我的源代码,个人觉得代码注释已非常详细,很容易就能看懂。
- 由于没有用 scrapy 框架,而是用的单个文件,所以我把 获取标签的总分类单独用一个文件来写这个程序了。然后把获取书本信息也单独用一个文件来写了。其实这个并不影响。因为我们可以通过导入文件来是两个 .py 文件能够连接起来。具体导入操作:
- 通过sys模块来完成: 打开计算机–>cmd–>输入一下指令
# 先进入Python环境>>> ipython>>> import sys>>> # 查看所有包路径>>> sys.path>>> # 添加刚才自己所写的包的路径>>> sys.path.append("D://douban/")
在完成上面的添加之后,在某一个程序文件中就可以用 import 来导入我们想导入的文件了。给出我获取总分类标签的源码:
''' 将标签信息储存到表格 '''import urllib.requestimport refrom lxml import etree # 使用xpath筛选器from user_agent.base import generate_user_agentfrom openpyxl import Workbook, load_workbook # 用于创建 和 读取 表格文件from openpyxl.styles import colors, Font, Alignment, Border, Side # 改变字体颜色,大小, 对其方式, 边框goabal tag_listtag_list = [] # 用来存储所有分类标签下的子标签def url_open(url): head = {"User-Agent": generate_user_agent()} req = urllib.request.Request(url, headers=head) response = urllib.request.urlopen(req).read() return response# 获取首页书签,并存为表格def get_mark(url): response = url_open(url).decode('utf-8') html = etree.HTML(response, parser=None, ) # 获取标签总分类的列表 categories = html.xpath('//a[@class="tag-title-wrapper"]/@name') # 获取《文学》下的标签分类 # 匹配出文学分类下包含的所有标签内容 literature_string = re.compile('(<a name="文学" class="tag-title-wrapper">\s(\s|.)*?\s</div>)').findall(response)[0][0] # 实例化对象为可xpath操作的对象,xpath返回列表 html = etree.HTML(literature_string, parser=None, ) literature_string = html.xpath('//td/a/text()') # 保存第一个标签分类 yield save_main_mark(row=main_row_start, value=categories[0]) # 保存第一个标签下的子标签 yield save_mark(literature_string) # 获取《流行》下的标签分类 popular_string = re.compile('(<a name="流行" class="tag-title-wrapper">\s(\s|.)*?\s</div>)').findall(response)[0][0] html = etree.HTML(popular_string, parser=None, ) popular_string = html.xpath('//td/a/text()') yield save_main_mark(row=main_row_start, value=categories[1]) yield save_mark(popular_string) # 获取《文化》下的标签分类 culture_string = re.compile('(<a name="文化" class="tag-title-wrapper">\s(\s|.)*?\s</div>)').findall(response)[0][0] html = etree.HTML(culture_string, parser=None, ) culture_string = html.xpath('//td/a/text()') yield save_main_mark(row=main_row_start, value=categories[2]) yield save_mark(culture_string) # 获取《生活》下的标签分类 life_string = re.compile('(<a name="生活" class="tag-title-wrapper">\s(\s|.)*?\s</div>)').findall(response)[0][0] html = etree.HTML(life_string, parser=None, ) life_string = html.xpath('//td/a/text()') yield save_main_mark(row=main_row_start, value=categories[3]) yield save_mark(life_string) # 获取《经管》下的标签分类 manage_string = re.compile('(<a name="经管" class="tag-title-wrapper">\s(\s|.)*?\s</div>)').findall(response)[0][0] html = etree.HTML(manage_string, parser=None, ) manage_string = html.xpath('//td/a/text()') yield save_main_mark(row=main_row_start, value=categories[4]) yield save_mark(manage_string) # 获取《科技》下的标签分类 technology_string = re.compile('(<a name="科技" class="tag-title-wrapper">\s(\s|.)*?\s</div>)').findall(response)[0][0] html = etree.HTML(technology_string, parser=None, ) technology_string = html.xpath('//td/a/text()') yield save_main_mark(row=main_row_start, value=categories[5]) yield save_mark(technology_string)# 传入分类value,储存主分类,文学,流行...etcdef save_main_mark(row, value): # 如果是第一次就创建表格 if main_row_start == 1: wb = Workbook() # 创建工作表 ws1 = wb.active # x选中工作表中的第一个sheet,_active_sheet_index属性默认为 0 ws1.title = "标签" # 更改sheet1的名字为标签 ws1.sheet_properties.tabColor = "1072BA" # sheet1的背景颜色,有RRGGBB确定 else: wb = load_workbook(filename="豆瓣图书.xlsx",) ws1 = wb.active # x选中工作表中的第一个sheet,_active_sheet_index属性默认为 0 ws1.merge_cells(start_row=row, start_column=1, end_row=row, end_column=4) # 合并单元格 A1-D1 ,ws1.merge_cells('A1:D4') ws1.cell(column=1, row=row, value=value).font=Font(color=colors.RED, italic=None, size=18, bold=True,) # 操作单元格,红色,加粗 ws1.cell(column=1, row=row, value=value).alignment = Alignment(horizontal="center", vertical="center") wb.save(filename="豆瓣图书.xlsx")row_start = 2 # 从第二行开始main_row_start = 1 # 标签分类从一行开始# 存储子分类。def save_mark(x): # 定义全局变量方便每次都能按顺序自动存储 global row_start global main_row_start tag = 0 wb = load_workbook(filename="豆瓣图书.xlsx",) ws1 = wb.active # 设置第row行行高为30 # ws1.row_dimensions[row].height = 30 # 设置第col列列宽为20 for col in 'ABCD': ws1.column_dimensions[col].width = 20 # 储存为 7*4 的表格 for row in range(row_start, row_start+10): for col in range(1, 5): # 设置字体 ws1.cell(row=row, column=col, value=x[tag]).font = Font(color='EE6A50', size=14, bold=True,) # 设置对其格式 ws1.cell(row=row, column=col, value=x[tag]).alignment = Alignment(horizontal="center", vertical="center") # 设置边界样式 ws1.cell(row=row, column=col, value=x[tag]).border = Border( top=Side(color='EE6A50'), left=Side(color='EE6A50'), right=Side(color='EE6A50'), bottom=Side(color='EE6A50')) tag += 1 # 将标签内容移位 if tag == len(x): # 判断数据是否保存完 main_row_start = row + 1 # row_start = main_row_start + 1 # wb.save(filename="豆瓣图书.xlsx") return None # 用来结束循环if __name__ == "__main__": url = "https://book.douban.com/tag/" save = get_mark(url) print("正在处理,请稍后...") for fun in save: # for循环会自动调用 next()方法,和处理StopIteration(溢出)异常 pass print("--------------Finished!---------------")
个人觉得使用Xpath比使用BeautifulSoup要方便,可能因为用习惯了。还有就是这里频繁的使用了 yield 语句,这样做事我后来改的,因为发现不然在处理表格格式的时候回比较麻烦。
还有一个比较需要注意的地方,就是 Python 如何退出循环语句,一开始以为像 C那样,只要再后面加一个 break 就万事大吉了,然而并没有什么卵用。结束循环的方法有两个:①,通过主动raise一个Error然后对这个Error进行处理。 ②,通过return 语句
这里创建了一个tag_list的数组,用来存储所有的标签,方便另一个程序使用这些标签来生成对应链接。达到前面所说的目的。不过我没有进行这一步,因为这可以留到后面优化程序的时候做。但是,后面出现了个意外,在我调试这个程序的时候 IP被被封了,所以就没弄了。。。等风头过去再来试试。下个准备写个获取代理IP的,这样就不怕了。。。好啦,下面是部分效果图:
关于表格有关操作,有时间再单独写一篇,这里就不多说了,因为我也是自己一步一步慢慢跟着官方文档摸索的,所以在代码中有关表格的地方都写的很详细。我觉得不能再详细。。。不然全是中文了。有想法的可以去看下openpyxl 的官方文档。openpyxl官方文档
II。书本基本信息的获取
- 这里使用了多线程的另一种形式,那就是创建类,然后用来继承 threading.Thread 这个类,通过重写override它的run方法来实现多线程。
- 这里一个使用了三个类,也就是线程。
- 第一个,Book() 用来获取基本信息,书名,作者,星级.etc
第二个,Save() 用来保存获取到的信息到表格,只保存某一页
第三个,Hanlder() 用来处理保存数据之后的表格,达到美观的效果
其实这里如果爬取某一页的话 开三个线程是没有必要的,因为三个程序都有必然的先后顺序关系,并不能实现并行执行,所以这里只是为了看上去更加直观。当然,如果要爬去所有的页面数据的话,还是可以实现伪并行的。为什么叫伪并行呢…具体看代码就知道了。中间我使用了 queue队列来储存信息,这样方便数据的提取。跟前面一样,代码的注释非常详细,所以这里给出代码:
'''表格格式为:title,author,rating_nums, comment_nums, link'''import urllib.request, urllib.parseimport urllibfrom user_agent.base import generate_user_agentimport threadingfrom lxml import etreefrom openpyxl import Workbook, load_workbook # 导入创建和加载工作簿的库文件from openpyxl.styles import Font, colors, Alignment # 导入工作簿样式所需的库文件,字体,颜色,对齐方式from openpyxl.worksheet.table import Table, TableStyleInfo # 导入工作表中制作表格所需的库文件,table, 表格风格import queue # 产生队列,先进先出.Queue()information = queue.Queue()url_queue = queue.Queue()import timeimport re# 获取内容标签下某一页内容class Book(threading.Thread): def __init__(self, url, queue): super().__init__() self.url = url self.queue = queue def url_open(self, url): head = {"User-Agent" : generate_user_agent()} req = urllib.request.Request(url, headers=head) response = urllib.request.urlopen(req).read() return response def run(self): print("正在获取网站信息...\n地址:%s\n" % self.url) response = self.url_open(self.url).decode('utf-8') # 使用HTML解析网页 response = etree.HTML(response, parser=None) book_name = response.xpath('//h2/a/@title') # 对获取的数据进行格式处理 book_author = [] book_authors = response.xpath('//div[@class="pub"]/text()') for each in book_authors: temp = each.replace(' ','').strip() book_author.append(temp.split("/")[0]) rating_num = [] rating_nums = response.xpath('//span[@class="rating_nums"]/text()') for each in rating_nums: rating_num.append(float(each)) comment_num = [] comment_nums = response.xpath('//span[@class="pl"]/text()') for each in comment_nums: comment_num.append(int(''.join(re.findall(r'[0-9]', each)))) book_link = response.xpath('//h2/a/@href') info_zip = list(zip(book_name, book_author, rating_num, comment_num, book_link)) self.queue.put(info_zip,timeout=None,) self.queue.task_done()# 用来保存某一页的内容class Save(threading.Thread): def __init__(self, queue, filename, sheetname): super().__init__() self.queue = queue self.filename = filename self.sheetname = sheetname def run(self): i = 2 wb = load_workbook(filename=self.filename) # 打开已有表格 ws2 = wb.create_sheet(title=self.sheetname) # 添加第一行数据 ws2.merge_cells("E1:I1") ws2.append(("书名", "作者", "豆瓣评分", "评价人数", "链接地址")) # 判断数据是否存完,True则等待,False 则继续 while self.queue.empty() is False: print("正在保存数据到表格,请稍后...") content = self.queue.get() # 从队列中取数据 print("此页包含 %d 个数据" %len(content)) for row in content: ws2.merge_cells(start_column=5, start_row=i, end_column=9, end_row=i) i += 1 ws2.append(row) # 添加行数据 wb.save(filename=self.filename) print("数据保存完成!") hanlder.start() hanlder.join()# 用来处理文档的格式class Hanlder(threading.Thread): def __init__(self, queue, filename, sheetname): super().__init__() self.queue = queue self.filename = filename self.sheetname = sheetname def run(self): try: if self.queue.empty() is True: # 若数据已保存完毕 wb = load_workbook(filename=self.filename) # 打开已有表格 print("正在处理表格样式,请稍后...") ws2 = wb[self.sheetname] # 选择某一个工作表 ws2 = wb.worksheets[1] 通过索引获取或名字wb["name"] ws2.sheet_properties.tabColor = "DDA0DD" # 设置工作表背景色 # 设置列宽 ws2.column_dimensions['A'].width = 20 ws2.column_dimensions['B'].width = 28 ws2.column_dimensions['C'].width = 12 ws2.column_dimensions['D'].width = 15 ws2.column_dimensions["E"].width = 20 # 设置对齐与字体 for cells in ws2["A1:E1"]: for cell in cells: cell.alignment = Alignment(horizontal="center", vertical="center") # 第一行对齐 cell.font = Font(size=16, color="A020F0", bold=True) row_length = (ws2.max_row) # 获得包含数据的总行数int sheet.rows 为返回所有行(含数据)可用来迭代 col_length = ws2.max_column # 获取总列数 for row in range(2, row_length+1): for col in range(1, col_length+1): ws2.cell(row=row, column=col).alignment = Alignment(horizontal="center", vertical="center") # 居中对齐 ws2.cell(row=row, column=col).font = Font(size=12, color="B452CD", bold=True) # 字体 ws2.cell(row=row, column=col_length).alignment = Alignment(horizontal="left", vertical="center") # 最后一列左对齐 ''' 因为 表中包含数字,而openpyxl规定,ref过滤器范围必须始终包含字符串,否则Excel会报错并删除表格,应该是这样 # 制作成表格 # Add a default style with striped rows and banded columns style = TableStyleInfo(name="TableStyleMedium9", showFirstColumn=False, showLastColumn=False, showRowStripes=True, showColumnStripes=True) tab = Table(displayName="书名信息表", ref="A1:C21",tableStyleInfo=style) ws2.add_table(tab) ''' wb.save(self.filename) print("格式处理完成!") except PermissionError as e: print("表格已被打开,请先关闭! %s" %e)if __name__ == "__main__": tag = "小说" tag_encode = urllib.parse.quote(tag) # 编码中文 filename = "豆瓣图书.xlsx" sheetname = tag for page in range(0, 1000, 20): url = "https://book.douban.com/tag/{tag}?start={int}".format(tag=tag_encode, int=page) Book(url=url, queue=information).start() save = Save(queue=information, filename=filename, sheetname=sheetname) hanlder = Hanlder(queue=information, filename=filename, sheetname=sheetname) time.sleep(1) save.start()
这里要注意的是,小说分类下的网站是含有中文名的,所以需要处理,对中文进行编码。通过 urllib.parse.quote()。
目标获取 小说 标签下的1000本图书,不过实际过得为997本,发下部分效果图吧:
还有就是,上面 Book()中 获取到的数据都通过了处理,这里看下网页结构就明白了,感觉该说的代码里都已经注释了,写的不好,还请指正。
如果有需要的话可以看看源码,个人觉得思路还是比较清晰的。至于如何过去其他标签下的内容,换汤不换药,这里就不说了。因为现在连豆瓣网都进不去。。。[\笑哭]
在这里记录下自己的学习过程,希望遇到相同问题的朋友能得到帮助,也希望自己能学的更好。好啦,就这么多吧
- Python爬取豆瓣图书信息学习记录
- python爬取豆瓣图书
- Python爬虫入门 | 4 爬取豆瓣TOP250图书信息
- 爬取豆瓣图书Top250书籍信息
- python爬取豆瓣图书Top250
- <Python爬虫>爬取豆瓣图书/豆瓣电影系列
- Python爬虫爬取豆瓣图书的信息和封面,放入MySQL数据库中。
- scrapy爬取豆瓣读书的图书信息
- python爬取豆瓣电影信息
- python爬取豆瓣电影信息
- python:使用requests和bs4爬去豆瓣图书信息
- python爬虫之豆瓣图书信息几行字
- 爬取豆瓣Top250图书【Beautiful】
- python 爬虫学习三(Scrapy 实战,豆瓣爬取电影信息)
- python爬取的豆瓣top250的一些信息
- python爬虫实战 | 爬取豆瓣TOP250排名信息
- python爬虫爬取豆瓣书籍信息并生成表格
- Python爬虫入门 | 2 爬取豆瓣电影信息
- java二维码生成-谷歌(Google.zxing)开源二维码生成的实例及介绍
- patch -p0 和patch -p1的区别 patch 用法
- MATLAB自定义colorbar
- phpcms PC标签调用说明
- Spring_day01
- Python爬取豆瓣图书信息学习记录
- PHP删除目录及目录下所有文件或删除指定文件方
- 一种STM32的串口控制台的实现(非常实用)
- centos7 安装python2.6,与系统自带的python2.7共存
- JavaScript 开发人员需要知道的简写技巧
- HTTP协议总结
- js中的call与apply
- mysql基本函数总结
- pip安装软件