国产秋霞理论久久久电影-婷婷色九月综合激情丁香-欧美在线观看乱妇视频-精品国avA久久久久久久-国产乱码精品一区二区三区亚洲人-欧美熟妇一区二区三区蜜桃视频

用 Python 將微信熱文轉(zhuǎn)換成Word文檔 | 神級操作

共 4095字,需瀏覽 9分鐘

 ·

2020-11-23 16:51


不得不說微信公眾號已經(jīng)成為了一個開放平臺,每天數(shù)以萬計的微信公眾號文章在這產(chǎn)生,我們關(guān)注一個微信公眾號每天便可以看到新的文章,我們同時也不知不覺的將好的文章分享到給朋友。


那么如何保存一個好的文章呢?普遍選擇收藏,然而在這里,我提供一個更巧妙的方法,直接轉(zhuǎn)換成word文檔保存在電腦里面。即便是以后文章404了,我們還可以看得到嘛。


1、微信熱文源代碼分析



一篇微信文章,url開頭一定是https://mp.weixin.qq.com/s/,后面跟著一長串字符串,比如qLjifoyinoVN5i5vjW0f7w。

查看網(wǎng)頁源代碼,我們發(fā)現(xiàn)




微信熱文的網(wǎng)頁源代碼很長,即便是上面的一個很簡短的文章,但我們要從中提取到我們想要的東西,比如

<h2id="activity-name">普京再次出面h2>

妥妥的文章題目,我們要把它保存為word文檔,題目肯定少不了。

<div id="js_profile_qrcode"class="profile_container" style="display:none;">
<div>
<strong>環(huán)球時報strong>
<imgid="js_profile_qrcode_img" src="" alt="">
<p>
<label>微信號label>
<spanclass="profile_meta_value">hqsbwxspan>p>
<p>
<label>功能介紹label>
<span>報道多元世界 解讀復(fù)雜中國span>p>
div>


這里一下子就提示了這篇文章是那個微信號發(fā)布的,而且還有微信號的介紹,這也是我們需要的信息

<div id="js_content" style="visibility: hidden;">

這個就是正文的標(biāo)簽了,這個標(biāo)簽里面蘊含著正文,下面是正文的第一個標(biāo)簽,我們將它格式化一下,如下



我們發(fā)現(xiàn)section套了很多層,但是實際上,這第一個標(biāo)簽就這一句話是重點:“俄總統(tǒng)普京同納卡沖突雙方領(lǐng)導(dǎo)人舉行電話會談?!?/p>



下一個標(biāo)簽也是section,但是涵蓋了好幾句話。我們發(fā)現(xiàn)了span標(biāo)簽和strong標(biāo)簽。而且出現(xiàn)了很多次rgb(),我們知道rgb是代表標(biāo)簽內(nèi)字體的顏色的。當(dāng)然,strong是標(biāo)簽內(nèi)加粗咯。


<img data-ratio="0.7717391304347826" data-s="300,640"data-type="jpeg" data-w="828" data-backw="578"data-backh="446" src="https://mmbiz.qpic.cn/mmbiz_jpg/qkQTRn2Z9NwC8nNHScsBAFeOFtHHb95ftWKOZve0QJMqJPFtoicdYO8uTWom8fBdG07icCKDo0FoyNjHUyoBibI2g/640?wx_fmt=jpeg"style="text-align: center;width: 660.994px;box-sizing: border-box!important;visibility: visible !important;"  />


另一個圖片標(biāo)簽


<img data-ratio="1.345"src=""color: rgb(249, 38, 114);">mmbiz.qpic.cn/mmbiz_gif/wlCrBZoK8HF5AE2ibhItnFJgoIQBcJhTzO438azQniaRJRYNFk0CzlORnm0g1hG7HX3bhXAIC1J4E2XGb1WKA4qA/640?wx_fmt=gif"data-type="gif" data-w="200" style="vertical-align:middle;box-sizing: border-box;" />


這個是圖片的標(biāo)簽,里面蘊含著很多重要的東西,比如,data-type="gif",表明這是一個gif文件,src指向了圖片的地址,data-w="200",代表圖片的寬度,這很重要。

格式化后的內(nèi)容如下所示




標(biāo)簽套標(biāo)簽,讓人眼花繚亂。

不過,還是一步一步來吧。


2、設(shè)計代碼,步步分析


這一步我們需要開始編寫代碼了,python-docx是一個生成和處理docx的第三方庫,使用pip install python-docx 一鍵下載

需要用到的第三方庫有,python-docx,bs4(用于html解析處理)

from docx import Document
from docx.oxml.ns import qn
import re
from docx.shared import RGBColor,Inches,Pt
from urllib.request import urlopen,Request
from bs4 import BeautifulSoup
from docx.enum.text import WD_PARAGRAPH_ALIGNMENT
import io
from os.path import join



qingqiu={'User-Agent':"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11",
'Accept':'text/plain, text/html'
}


編寫一個簡單的過濾函數(shù),因為我們得到文章標(biāo)題后,需要將文章標(biāo)題中一些字符刪去,比如換行符,空格,以及{}?

/|\等字符,因為含有這些字符的字符串不能做文件名

def guolv(text):
t = re.sub('\s', '', text)
t = re.sub('[?<>()[\]{}|]', ':', t)
return t


假設(shè)微信url已經(jīng)確定,在這里我們編寫一個類,這個類專門用來處理的。


class WX_doc():
def __init__(self, url, path):
self.img_num = 0
self.doc = Document()
self.doc.styles['Normal'].font.name = '微軟雅黑'
self.doc.styles['Normal']._element.rPr.rFonts.set(qn('w:eastAsia'), '微軟雅黑')
self.url = url
self.path = path

self.img_num是針對img處理的,每處理一個img,self.img_num+=1,請注意,最好設(shè)置好文章的字體,因為python-docx默認(rèn)字體顯示中文會比較難看……不信你可以去試試。當(dāng)然也可以將字體設(shè)為宋體

url是指微信熱文的鏈接,path是Word文檔處理完后的保存路徑。

接下來是一個插入一個標(biāo)題的方法。

注:

我們設(shè)單獨的def開頭的為函數(shù),包含在class內(nèi)的def開頭的為方法

def head(self, title, lv=3, size=13):
p = self.doc.add_heading('', lv)
p.paragraph_format.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
r = p.add_run(title)
r.font.name = '微軟雅黑'
r.font.color.rgb = RGBColor(0, 0, 0)
r.font.size = Pt(size)
r._element.rPr.rFonts.set(qn('w:eastAsia'), u'微軟雅黑')

將標(biāo)題插入后,設(shè)置為居中,顏色黑色,大小默認(rèn)13,字體微軟雅黑。

?

2、巧妙處理標(biāo)簽

對于正文來講,標(biāo)簽套標(biāo)簽讓人眼花繚亂,然而我們?nèi)绾翁幚碚闹械奈淖?,圖像甚至表格呢?

,對于標(biāo)簽套標(biāo)簽,我設(shè)計的思路是:

用對應(yīng)的標(biāo)簽方法處理標(biāo)簽

hd = Request(self.url, headers=qingqiu)
a = urlopen(hd)
b = a.read()
bb = b.decode('UTF-8')
bs = BeautifulSoup(bb, 'lxml')
h2 = bs.find('h2', {'class': "rich_media_title"})
title = guolv(h2.text)
self.head(title, 2, 18)

pingtai = bs.find('strong', {'class': "profile_nickname"})
PMV=bs.findAll('span',{'class':'profile_meta_value'})
p = self.doc.add_paragraph()
r = p.add_run('%s' % pingtai.text)
r.font.bold = True
r.font.color.rgb = RGBColor(0, 191, 255)
r.font.size = Pt(12)
r=p.add_run('(%s: %s)'%(PMV[0].text,PMV[1].text))
r.font.size = Pt(9)

wb = bs.find('div', {'class': "rich_media_content"})

這樣一處理,bs就是整篇微信文章的BeautifulSoup結(jié)構(gòu)的html,這樣處理就方便的多。

對于標(biāo)題和發(fā)布者的,我們放到后面處理,現(xiàn)在要考慮正文的處理,wb就是正文的bs結(jié)構(gòu)。

如何編寫標(biāo)簽函數(shù)?我假定只關(guān)注字體的顏色和加粗,其余字體大小不考慮(這樣的話保存的文章樣式是一致的),使用RGB代表顏色,比如RGB=(0,0,0)就是純黑了,bold代表加粗,bold=True就是加粗。


標(biāo)簽


p代表段落,p標(biāo)簽內(nèi)的文字會形成一個段。對應(yīng)doc中的add_paragraph方法,接下來我們編寫WX_doc的第一個標(biāo)簽處理方法。默認(rèn)字體顏色黑色,不加粗。

def para(self, label):
p = self.doc.add_paragraph('')
for i in label:
self.transit(i,p, (0, 0, 0), False)

這樣就完了,主要操作就是,將p中每一個標(biāo)簽?zāi)贸鰜?,交給transit函數(shù)處理,transit會針對相應(yīng)的標(biāo)簽交給相應(yīng)的標(biāo)簽方法。

但是如果出現(xiàn)這樣的情況,p內(nèi)含p,就像section一樣一層套一層,那么需要另一個p處理方法

def para2(self,label,p,RGB,bold):
"解決p內(nèi)含p的情況"
for i in label:
self.transit(i,p, RGB, bold)

對于后面的標(biāo)簽處理方法,我們規(guī)定,需4個參數(shù),第一個BeautifulSoup結(jié)構(gòu)的標(biāo)簽label,第二個,所屬的段落p,為doc.add_paragraph方法返回的段落p,第三個和第四個為RGB和bold。


標(biāo)簽


Span標(biāo)簽出險率極高,基本上每段文字都會出現(xiàn),我們假定span中的style設(shè)定文字的顏色。

比如這一段span

<spanstyle="letter-spacing: 1px;font-size: 16px;font-family: helvetica;color: rgb(123,12, 0);"><strong>普京與兩國領(lǐng)導(dǎo)人討論了本月9日三方簽訂的?;饏f(xié)議落實問題。各方對當(dāng)前沖突接觸線的平靜局勢感到滿意。strong>span>

多次觀察后,編寫的處理方法如下

def span(self, label, p, RGB, bold):
attr = label.attrs.get('style')
if attr:
ys = re.findall('(?<=rgb\()[\s\S]+?(?=\))', attr)
else:
ys=[]
if ys:
rgb = re.findall('\d+', ys[0])
r = int(rgb[0])
g = int(rgb[1])
b = int(rgb[2])
RGB = (r, g, b)
for i in label:
if i.name == None:
self.text(i,p, RGB, bold)
elif i.name == "strong":
self.strong(i,p, RGB, bold)
else:
self.transit(i,p, RGB, bold)

當(dāng)BeautifulSoup結(jié)構(gòu)下的標(biāo)簽結(jié)構(gòu)為None時,它就是一段純文字

?

Text 純文字處理


處理純文字用的方法,需要注意的是,要將文字中的換行符刪去。

def text(self, i, p, RGB, bold):
i=str(i)
i=i.replace('\n','')
r = p.add_run(i)
r.font.bold = bold
r.font.color.rgb = RGBColor(RGB[0], RGB[1], RGB[2])


標(biāo)簽


Strong就是加粗

def strong(self, label, p, RGB, bold):
for i in label:
if i.name == None:
self.text(i,p, RGB, True)
elif i.name == 'span':
self.span(i,p, RGB, True)


標(biāo)簽

Section常常會出現(xiàn)套疊的情況,即便是里面有字體顏色大小的指示,我還是以span指示的顏色為準(zhǔn)。那么如何正確處理section便是一個難題。

<sectionstyle="font-family: -apple-system-font, BlinkMacSystemFont, &quot;HelveticaNeue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino SansGB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;MicrosoftYaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;white-space:normal;background-color: rgb(255, 255, 255);line-height: 1.5em;margin-left:0px;margin-right: 0px;">
<span style="color: rgb(136,136, 136);font-family: helvetica;font-size: 14px;font-weight:700;letter-spacing: 1px;text-align: left;text-indent: 28px;widows: 1;">span>
<span style="color: rgb(136,136, 136);font-family: helvetica;font-size: 14px;font-weight:700;letter-spacing: 1px;text-align: left;text-indent: 28px;widows: 1;">俄總統(tǒng)網(wǎng)站聲明截圖span>section>

上面的section中出現(xiàn)了span,所以思路來了,遍歷section中的標(biāo)簽,如果出現(xiàn)span和stong,直接按段落處理

def section(self,label):
for i in label:
if i.name=='p':
self.para(i)
elif i.name in ['span','strong']:
self.para(label)
return 0
elif i.name=='section':
self.section(i)
elif i.name in ['ul','ol']:
self.ul2(i)
elif i.name=='img':
self.img(i)
elif i.name in ['br','svg','center']:
pass
elif i.name=='blockquote':
self.blockquote(i)
elif i.name=='pre':
self.pre(label)
else:
print('section中:%s:%s'%(i.name,str(i)))

最后else表示沒有這個標(biāo)簽的處理函數(shù),就提示這個標(biāo)簽的位置,以及名稱,所含內(nèi)容

?

mmbiz.qpic.cn/mmbiz_jpg/qkQTRn2Z9NwC8nNHScsBAFeOFtHHb95ftWKOZve0QJMqJPFtoicdYO8uTWom8fBdG07icCKDo0FoyNjHUyoBibI2g/640?wx_fmt=jpeg"style="text-align: center;width: 660.994px;box-sizing: border-box!important;visibility: visible !important;" />
<imgdata-ratio="1.345"src=""color: rgb(249, 38, 114);">mmbiz.qpic.cn/mmbiz_gif/wlCrBZoK8HF5AE2ibhItnFJgoIQBcJhTzO438azQniaRJRYNFk0CzlORnm0g1hG7HX3bhXAIC1J4E2XGb1WKA4qA/640?wx_fmt=gif"data-type="gif" data-w="200" style="vertical-align:middle;box-sizing: border-box;" />

我們發(fā)現(xiàn)data-w是設(shè)定圖片的寬度,當(dāng)圖片過大的時候,需要將圖片寬度設(shè)定好。Img處理函數(shù)如下

def img(self, label):
src = label.attrs['src']
da_s = label.attrs.get('data-s')
data_type = label.attrs.get('data-type')
data_w = label.attrs.get('data-w')
self.img_num += 1
a = urlopen(src)
b = a.read()
path = io.BytesIO(b)
if da_s:
num = re.findall('\d+', da_s)
h = int(num[0]) // 75
w = int(num[1]) // 75
if w > 6:
self.doc.add_picture(path, width=Inches(6))
else:
self.doc.add_picture(path, width=Inches(w), height=Inches(h))
elif data_w:
data_w = int(data_w)
if data_w < 75:
# 標(biāo)簽太小,直接忽略
print('忽略太小圖片%d.%s' % (self.img_num, data_type))
elif data_w > 450:
self.doc.add_picture(path, width=Inches(6))
else:
self.doc.add_picture(path, width=Inches(data_w / 75))
else:
self.doc.add_picture(path, width=Inches(6))
print("圖片%d打入成功!" % (self.img_num - 1))


transit方法


最后我們編寫transit方法

def transit(self, label, p, RGB, bold):
"本函數(shù)提供label的中轉(zhuǎn)方案 其中br由中轉(zhuǎn)方案解決"
if label.name == 'span':
self.span(label, p,RGB,bold)
elif label.name == None:
self.text(label, p,RGB,bold)
elif label.name in ['strong','em']:
self.strong(label, p,RGB,bold)
elif label.name=='section':
self.section(label)
elif label.name =='p':
self.para2(label,p,RGB,bold)
elif label.name == 'img':
self.img(label)
elif label.name in ['br','svg','mpcpc','center']:
pass
elif label.name == 'a':
self.link(label, p,RGB,bold)
elif label.name == 'iframe':
self.iframe(label, p)
elif label.name == 'blockquote':
self.blockquote(label)
elif label.name == 'ul':
self.ul(label, p)
elif label.name=='pre':
self.pre(label)
else:
print('p中:%s %s'%(str(label.name),str(label.text)))
t = label.text
if len(t) < 2:
return 0
r = p.add_run(t)
r.font.bold = bold
r.font.color.rgb = RGBColor(RGB[0], RGB[1], RGB[2])

transit函數(shù)要處理一個標(biāo)簽,如果已經(jīng)編寫好了這個標(biāo)簽方法,那么將這個標(biāo)簽交給對應(yīng)的標(biāo)簽方法處理,如果沒有,就提示這個標(biāo)簽的位置,以及名稱,所含內(nèi)容

?

main 核心處理


最后我們當(dāng)然是處理并且轉(zhuǎn)換成文檔啦,加入文章標(biāo)題,發(fā)布者,和內(nèi)容,直接發(fā)完整代碼吧,如下:

def main(self) -> None:
hd = Request(self.url, headers=qingqiu)
a = urlopen(hd)
b = a.read()
bb = b.decode('UTF-8')
bs = BeautifulSoup(bb, 'lxml')
h2 = bs.find('h2', {'class': "rich_media_title"})
title = guolv(h2.text)
self.head(title, 2, 18)
pingtai = bs.find('strong', {'class': "profile_nickname"})
PMV=bs.findAll('span',{'class':'profile_meta_value'})
p = self.doc.add_paragraph()
r = p.add_run('%s' % pingtai.text)
r.font.bold = True
r.font.color.rgb = RGBColor(0, 191, 255)
r.font.size = Pt(12)
r=p.add_run('(%s: %s)'%(PMV[0].text,PMV[1].text))
r.font.size = Pt(9)

wb = bs.find('div', {'class': "rich_media_content"})
for i in wb:
if i.name =='p':
self.para(i)
elif i.name=='section':
self.section(i)
elif i.name == 'blockquote':
self.blockquote(i)
elif i.name == 'table':
self.table(i)
elif i.name in[None,'center','hr']:
pass
elif i.name in ['h1', 'h2', 'h3','h4']:
self.head(i.text, int(i.name[1]) + 1)
elif i.name in ['ul','ol']:
self.ul2(i)
elif i.name == 'pre':
self.pre(i)
else:
print("%s"%str(i))
self.save_docx(title)
wz_pa=join(self.path,title+'.docx')
print('文檔保存成功!保存路徑:%s'%wz_pa)
self.ok=False
print(wz_pa)


3、實戰(zhàn)測試


運行后輸入微信url,結(jié)果如下:



保存下來的Word文檔如下:



4、其他標(biāo)簽的處理說明


剛剛我們僅僅是編寫了section,span,p,strong等標(biāo)簽,就可以對付一個簡單的文章,但是實際上還有其他的標(biāo)簽,僅僅是這篇文章沒出現(xiàn)而已。所以為了讓這程序越來越好,我們需要添加一些標(biāo)簽處理的方法。


標(biāo)簽


Blockquote代表著引用,比如文章引用的哪句話,抄了哪些文獻(xiàn)的句子,都用這個標(biāo)簽。為了和正文區(qū)別,我將字體大小設(shè)置為9默認(rèn)顏色(100,100,100)

def blockquote(self, label):
"定義一個摘自另一個源的塊引用"
p = self.doc.add_paragraph('')
p.style.font.size = Pt(9)
for i in label:
self.transit(i,p,(100,100,100),False)

?