python爬取小说写入txt_06 Python3.6爬取全言乒乓连载文章并保存pdf和txt电子书

思路:

这一阶段主要利用selenium来模拟Chrome浏览器获取所有的文章链接。首先要模拟点击不同的页内标签(如红色标注所示),但是由于每个标签下只默认显示十条,只有向下滚动触发js才能加载页内剩余的条目,这个过程属于异步加载。

AAffA0nNPuCLAAAAAElFTkSuQmCC

模拟点击不同的页内标签(如红色标注所示)

分析实现

这种规模的问题,一般会使用Beautifulsoup库+XHR调试或者selenium.webdriver,但是Beautifulsoup库+XHR调试有问题,在页面下滚捕捉query的时候,看起来像是有什么微妙的规律,但是真正更改query参数的时候,打开的网页还是一模一样,我不得其解,多究无益,果断止损放弃。

AAffA0nNPuCLAAAAAElFTkSuQmCC

更改query参数后,打开的网页还是一模一样,我不得其解,多究无益,果断放弃

于是敲定使用selenium。列表中读取文章链接,打开链接,抓取段落存入txt文件对象,网页利用weasyprint库直接转pdf;思路:这一步给定了文章链接,由于Beautifulsoup的速度比selenium要快(selenium要打开浏览器),我采用Beautifulsoup。pdf合并。

使用Pypdf2中的PdfFileMerger方法(from PyPDF2 import PdfFileMerger)合并pdf,但是这种方法不带书签。

如果执意添加书签超链接,需要from PyPDF2 import PdfFileReader, PdfFileWriter然后一遍addPage一边调用addBookmark,具体使用方法参考import timefrom selenium import webdriverfrom selenium.webdriver.common.keys import Keysimport osimport requestsfrom bs4 import BeautifulSoupfrom weasyprint import HTMLimport sslfrom PyPDF2 import PdfFileReader, PdfFileWriter, PdfFileMerger

outpath = './Table Tennis 24Years'  #输出到根目录指定文件夹,如果没有就创目录if not os.path.exists(outpath):

os.makedirs(outpath)

outpathpdf = './Table Tennis 24Years/PDF_folder'if not os.path.exists(outpathpdf):

os.makedirs(outpathpdf)#打开浏览器# 运行前先下载 chrome driver,下载地址是:https://sites.google.com/a/chromium.org/chromedriver/downloads,点击【Latest Release: ChromeDriver x.xx】进入下载driver = webdriver.Chrome(executable_path='/Users/miraco/PycharmProjects/grabnet/chromedriver')  # Windows 需写成'./chromedriver.exe'driver.start_client()  #网页需要模拟浏览器点击url_pages = ('https://mp.weixin.qq.com/mp/homepage?__biz=MzI5MjY0MTY1Ng==&hid=2&sn=858963d6283870bc173bbb7076a4e620&scene=25#wechat_redirect',            'https://mp.weixin.qq.com/mp/homepage?__biz=MzI5MjY0MTY1Ng==&hid=6&sn=53bfd170c878ae8b06c868cf8c5c4e34&scene=25#wechat_redirect'

)  #这是这两个目标网页的网址,我们要把网址里面的所有文章爬出来tops_css = '#namespace_1 > div.tab_hd > div > div'   #上方目录表标签样式titles_css = '#namespace_1 > div.tab_bd > div > a > div.cont > h2'   #标签下的题目的样式hrefs_css = '#namespace_1 > div.tab_bd > div > a'   #每个标签下的超链接样式info_css  = '#namespace_1 > div.tab_bd > div > a > div.cont > p'     #all_list = [] #这里面放所有文章的题目、链接、简介def pgdown():  #页面往下翻滚直到尽头,多次翻滚保证完全加载

html_page = driver.find_element_by_tag_name('html') #拿到网页对象

for i in range(8):

time.sleep(0.5)

html_page.send_keys(Keys.END)     #模拟对着网页按下键盘'END'的动作def find_art(url):  #要爬取给定url中的文章的题目、简介、超链接

lists = []  #这个列表里放要此url可达的文章的题目、梗概、链接

driver.get(url)  #打开其中一个网页

time.sleep(3)   #等待网页加载

buttons = driver.find_elements_by_css_selector(tops_css)  #找到上方目录表标签

for button in buttons:   #按个激活标签

time.sleep(2)    #等待网页加载

button.click()   #点击标签

pgdown()         #往下滚页

titles = driver.find_elements_by_css_selector(titles_css)  #找到所有每个标签下的题目对象

hrefs = driver.find_elements_by_css_selector(hrefs_css)    #找到每个标签下的超链接对象

intros = driver.find_elements_by_css_selector(info_css)    #找到每个题目下的简介对象

for title, href, intro in zip(titles,hrefs,intros):

txt = title.text          #题目对象转文本

if '):' in txt:          #因为正经文章题目有括号冒号字样,可以依此只找正经编号文章,不找其他

ref = href.get_attribute('href')    #超链接对象中提取超链接

lists.append([txt,ref,intro.text])  #符合要求的题目、超链接、简介作为一个子列表,放入大列表中

return listsfor url in url_pages:  #这是这两个目标网页的网址,都爬出来

all_list = all_list + find_art(url)    #得到的是[[a,b,c],[d,e,f],[,g,h,i],[j,k,l]]

#这里不能用append方法,因为用append以后得到的是[[[a,b,c],[d,e,f]],[[,g,h,i],[j,k,l]]]driver.quit()  #关浏览器print(all_list)   #这里打印放所有文章的题目、链接、简介#爬取到txt#建立或对已有的此名txt进行内容清空f = open(os.path.join(outpath,'Table Tennis 24 Years.txt'),'w')

f.close()#开写开爬,这里爬去使用selenium打开关闭浏览器太慢了,直接上Beautifulsoup,嗖嗖的f = open(os.path.join(outpath,'Table Tennis 24 Years.txt'),'a') #打开文件对象f.write('本文档内所有文章皆由"全言乒乓"撰写,Sober作为乒乓球迷苦于其内容支离分散,使用基于Python3.6网络爬虫工具进行文字整理,版权属于"全言乒乓",如侵权请联系我删除!\n\n\n')def web2txt(f,url,intro):  #给定txt对象、文章链接、简介,将其写入文件

web_page = requests.get(url)

soup = BeautifulSoup(web_page.text,'lxml')

title = soup.select('h2.rich_media_title')[0] #抓取文章页内的题目

f.write(title.text.strip() + ':' + intro.strip() + '\n\n')  #题目+简介写进文件

parapraghs = [i.text.strip() for i in soup.select('#js_content > p > span') if i.text.strip() != '' ]  #抓取段落列表并文本化,strip()去掉前后多余的空格

for paragraph in parapraghs:        if  '微信公众号' not in paragraph:      #判断本段是不是页末的广告

f.write(paragraph.strip()+'\n\n')    #不是广告才写进去

else:

f.write('\n------本节完------'+'\n\n')    #到广告了写上"本节完"

break

return f

ssl._create_default_https_context = ssl._create_unverified_context    #weasyprint有时候强制要求ssl,但是有时候会抽风犯错,为了避免ssl证书出问题,我们禁用sslfor title ,url, intro in all_list:

print(f'正在整理文章:{title}')  #表明进度

f = web2txt(f,url,intro)  #写txt,并依照题目命名

HTML(url).write_pdf(os.path.join(outpathpdf,f'{title}.pdf')) #写pdf,并依照题目命名f.close() #关闭文件对象,得到txt文件#再将pdf合并输出filelist = os.listdir(outpathpdf)   #读取文件夹里的文件名pdfs = [ os.path.join(outpathpdf,file) for file in filelist if not os.path.isdir(file)]   #摘取文件里的pdf放进列表pdfs.sort(key = lambda x : int(x.split('(')[1].split(')')[0])) #并按里面的数字排序,注意不能粗暴直接sort()排序,否则会出现10排在2前面的情况print(pdfs)#这段代码是直接合并pdf,不带书签的'''

merger = PdfFileMerger()

for pdf in pdfs:

merger.append(pdf)  #按pdf顺序合并

merger.write(os.path.join(outpath,'Table Tennis 24 Years.pdf'))  #合并输出

'''#这段代码是逐页合并pdf,而且有超链接书签的output  = PdfFileWriter()

output_Pages = 0  #文档总页数for pdf in pdfs:

input = PdfFileReader(open(pdf,'rb'))  #打开读取文档

pdf_name = pdf.split('/')[-1]    #拿到文件名称作为书签名

page_Count = input.getNumPages()    #读取当前小pdf的页数

output_Pages += page_Count     #总页数累加计数

for iPage in range(page_Count):

output.addPage(input.getPage(iPage))   #小pdf内逐页添加到输出pdf对象

output.addBookmark(pdf_name,pagenum =output_Pages-page_Count,parent=None)  #在小pdf的首页添加书签output.write(open(os.path.join(outpath,'Table Tennis 24 Years.pdf'), 'wb'))     #合并输出

运行结果txt

AAffA0nNPuCLAAAAAElFTkSuQmCC

txtpdf

AAffA0nNPuCLAAAAAElFTkSuQmCC

pdf

踩过的坑phantomJS

我一开始想要使用网页截图再转pdf,但是Webdriver的Chrome网页截图不支持滚动截图。其实selenium有两种形式,有头的和无头(headless)的,我用的是有头的浏览器,在以前开发者喜欢用的是PhantomJS,但是selenium不知道搞什么鬼,竟然在运行phantomJS时候提示最新版本的selenium停止支持js,建议我使用Chrome或者Firefox的无头浏览器,

无头浏览器就是没有界面的静默版本,也可以调用,但是肉眼看不见,不喜欢界面打扰的可以试试看下面的代码。chrome_options = Options()

chrome_options.add_argument('--headless')

chrome_options.add_argument('--disable-gpu')

driver = webdriver.Chrome(executable_path='./chromedriver',chrome_options=chrome_options)pdfkit

其实网页转pdf的库还有一个叫pdfkit,要预装wkhtmltopdf,而且转换效果很差,还不如这个库呢,不过weasyprint虽说效果更好,但是也是不支持异步加载的,有人在此项目的Github主页里issue了为什么不能加载微信文章的插图,作者也提到了这个问题,本库不支持js异步加载。爬取文章链接时的异步加载元素问题

在检测文章的入口的css元素样式的时候,如果点击了页上文章列表的新标签,那么在elements中所查找的css元素个数会增多,但是并不意味着你可以把列表标签挨个点击以后使用find_elements_by_css_selector方法一网打尽,你确实可以拿到元素,但是使用元素对象使用text方法以后,你发现只能从当前激活列表标签下的元素里拿出数据,不在当前页面的数据拿不出来,是空字符串'',所以只能点击一次拿一次。类似有人问过这样的问题,就是元素拿不到了。

因此,如果得到的文本只为空,那么当前定位的元素可能被隐藏了。判断是否被隐藏 。 driver.find_element_by_xx().is_displayed() ,如果得到 false的结果.那就说明被隐藏了。

怎么解决?is_displayed()为false的元素,依然可以通过getAttribute()方法获取元素的属性. 由于webdriver spec的定义,Selenium WebDriver 只会与可见元素交互,所以获取隐藏元素的文本总是会返回空字符串。可是,在某些情况下,我们需要获取隐藏元素的文本。这些内容可以使用element.attribute('attributeName'), 通过textContent, innerText, innerHTML等属性获取。weasyprint库直接转pdf时候ssl报错weasyprint.urls.URLFetchingError:

URLError: 

解决办法;禁用sslimport ssl

ssl._create_default_https_context = ssl._create_unverified_context

作者:夏威夷的芒果

链接:https://www.jianshu.com/p/bd3d88a66068

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值