概述
近日,社区收集个人想读的书籍信息,本人想到豆瓣上有此功能,但是又不能直接用豆瓣的地址,因而考虑将豆瓣书籍信息转存到excel表格中。
对于html的解析,在前面的《发现群组(三)》中已经有提及,在此不再赘述。本次主要了解到对excel的写操作,使用了xlwt包,由于本人使用Anaconda,相应的包已经有了,不需要单独安装。此外还有xlrd包,用于读取excel,本次没有使用。
代码的流程 :先从网页的title中解析出总的书籍数目,然后据此明确要获取的页面数目,依次获取每个页面,并解析其中的书名和出版信息,最终写入excel中。
后续改进的功能:表项的追加操作。
代码实现及输入文件
#!/usr/bin/python
# coding=utf-8
import urllib2
from bs4 import BeautifulSoup
import re
import codecs
import time
import xlwt
import string
#设置excel表格输出字符的样式
def set_style(name,height,bold=False):
style = xlwt.XFStyle() # 初始化样式
font = xlwt.Font() # 为样式创建字体
font.name = name # 'Times New Roman'
font.bold = bold
font.color_index = 4
font.height = height
borders= xlwt.Borders()
# borders.left= 6
borders.right= 60
# borders.top= 6
# borders.bottom= 6
style.font = font
style.borders = borders
return style
#建立和服务器的链接并获取页面
def GetHtmlPage(url):
#两种请求方式在请求的header不同,ajax异步请求比传统的同步请求多一个头参数:X-Requested-With
user_agent="Mozilla/5.0 (Windows NT 5.1; rv:44.0) Gecko/20100101 Firefox/44.0"
header={'User-Agent':user_agent,'X-Requested-With':'XMLHttpRequest'}
#url=
request=urllib2.Request(url,headers=header)
print url+'\n'
time.sleep(20) #延时两分钟解析下一个
try:
response=urllib2.urlopen(request)
except urllib2.URLError,e:
print e.code
print e.reason
return 0
else:
soup=BeautifulSoup(response.read(),"lxml")
return soup
#得到用户想读的总数目,由于目前豆瓣是每15条分一页的,因而根据总的数目解析出需要解析多少html页面
def GetTotalBookNum(soup):
filterName=re.compile(r'\s+') # r'\s+' 去掉title里面的空格及回车 ‘\s’匹配空格和回车 +表示多个
WishbookNum=re.compile(u'[^\d]+') #title的内容为 “username想读的书(书的总数)” ,这里通过此正则表达式将"书的总数"之外的字符去掉。
#例如 "刘诗诗想读的书(20)",经过处理后,得到数字20. \d匹配数字[0-9] ^表示非数字,+表示多个。
userName=filterName.sub('',soup.title.string)
print userName
bookNum= WishbookNum.sub('',userName)
print bookNum
return int(bookNum)
#文件写入到excel中
#入参: books :书籍名称列表; totalbooks:每个页面中书的总数目;sheet1:将要写入的excel的表单;pubs:书籍出版信息的列表
def SaveToExcel(books,totalBooks,sheet1,pubs):
for i in range(len(books)):
#print books[i]
sheet1.write(totalBooks+i+1,0,books[i]) #第一个参数为第几行,第二个参数为列。即往第一列的第x行中写入书的名称
for i in range(len(pubs)): #这里使用 range函数,取从[0,len(pubs))的值
#print pubs[i]
sheet1.write(totalBooks+i+1,1,pubs[i]) #同上,往第二列的第x行中写入出版信息。例如,第一次i=0;totalbooks=0,即写入第二行中;
#下载书籍信息
def UserBook(soup):
books=[]
killSpace=re.compile(r'\s+') #去除书名中的回车和空格
for li in soup('li'):
if ('class' in dict(li.attrs) and li['class']==['subject-item']): #针对页面中的<li>标签进行处理,找到存在书名的标签
bookInfo = (li.find('div', class_='info'))
book=killSpace.sub('',bookInfo.a.get_text()) #去除book信息中的回车和空格
books.append(book) #此处将书名添加到列表books中
return books #返回用户及他/它关注书名的列表
#下载pub信息
def BookPubInfo(soup):
pubs=[]
killSpace=re.compile(r'\s+') #去除出版信息中的回车和空格
for li in soup('li'):
if ('class' in dict(li.attrs) and li['class']==['subject-item']): #针对页面中的<li>标签进行处理,找到存在出版信息的标签
pubInfo = (li.find('div', class_='pub'))
pub=killSpace.sub('',pubInfo.get_text()) #去除出版信息中的回车和空格,注意此处直接是class的get_text,没有进入a的。具体可以参看豆瓣页面源码。
pubs.append(pub) #此处将出版添加到列表pubs中,append函数经常使用
return pubs #返回用户及他/它关注书名的列表
#
def SaveDoubanToExcel():
books=[]
pubs=[]
totalBooks = 0
f=xlwt.Workbook(encoding='GB18030') #创建工作簿,这里设置了编码方式,以便可以从excel中读写中文
sheet1 = f.add_sheet(u'xdi',cell_overwrite_ok=True)
row0=['书名','出版信息']
file=codecs.open('xdiurl.txt','r','utf-8') #打开存放url链接的文档
#往excel表单中的第一行写入数据。并调用设置字体类型的接口
for i in range(0,len(row0)):
sheet1.write(0,i,row0[i],set_style('Times New Roman',220,True))
sheet1.col(0).width=256*80 #此两行是设置表格的宽度,由于书名及出版信息较长,因而增加宽度到80
sheet1.col(1).width=256*80
for url in file.readlines(): #每行为一个url,针对每个url进行数据解析
url=url.strip() #此处语句很关键!!!用于去除回车换行。因为url文件中,每个地址占用一行,行末尾存在回车换行,导致后续字符串链接的时候会存在问题。
soup = GetHtmlPage(url)
if (0==soup): print 'fail to get html page %s\n' % url
else:
bookNum = GetTotalBookNum(soup)
bookPageNum= bookNum/15+1 #计算出总共有多少网页
for i in range(bookPageNum): #获取每页中的书籍信息
bookItemUrl=url+'?start=%d&sort=time&rating=all&filter=all&mode=grid' % (i*15) #这里i*15不加括号的话,会把前面引号部分的字符串重复15次!!优先级问题
soup = GetHtmlPage(bookItemUrl)
if (0==soup):print 'fail to get html page %s\n' % bookItemUrl
else:
pubs = BookPubInfo(soup)
books = UserBook(soup) #解析得到书名的列表
SaveToExcel(books,totalBooks,sheet1,pubs)
totalBooks +=len(books)
file.close()
f.save('book.xls')
if __name__ =='__main__':
SaveDoubanToExcel()
url.txt的文件内容为:
https://book.douban.com/people/xxxxx/wish
https://book.douban.com/people/xxxx/wish
https://book.douban.com/people/xxx/wish
效果
参考资料
1. 设置列宽、行高
3. 合并单元格的读写
以下为本文尚未用到的:
4. excel的图形化操作
5. 追加操作