利用BeautifulSoup合并多个带有交叉链接的html文件

合并多个html文件为一个html文件有很多种方式。如果多个html文件中带有交叉链接,简单将多个文件连接在一起很大概率导致交叉链接失效。本文介绍利用beautifulsoup和re模块合并多个含有交叉链接的html文件,并保证交叉链接的有效性。
本文所介绍的技巧在epub转换为pdf文件时经常可以用到。epub文件中交叉链接的A标签的href属性往往指明了html文件名,id属性则往往没有带上文件名,例如:

<?xml version="1.0" encoding="utf-8" standalone="no"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
  "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"><html xmlns="http://www.w3.org/1999/xhtml" xml:lang="zh-cn"><head>
  <title>目 录</title>
  <link href="../Styles/style0001.css" rel="stylesheet" type="text/css"/>
</head><body>
  <div>
    <h2>目 录</h2><br/>
    <h4><a href="part0004.xhtml">脂砚斋重评石头记 凡例</a></h4><br/>
    <h4><a href="part0005.xhtml">第一回 甄士隐梦幻识通灵 贾雨村风尘怀闺秀</a></h4>
    <h4><a href="part0006.xhtml">第二回 贾夫人仙逝扬州城 冷子兴演说荣国府</a></h4>
</div>
</body></html>

可以看到目录中href分别指向了part0004.xhtmlpart0005.xhtmlpart0006.xhtml等文件。再如:

<?xml version="1.0" encoding="utf-8" standalone="no"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
  "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"><html xmlns="http://www.w3.org/1999/xhtml" xml:lang="zh-cn"><head>
  <title>第一回 甄士隐梦幻识通灵 贾雨村风尘怀闺秀</title>
  <link href="../Styles/style0001.css" rel="stylesheet" type="text/css"/>
</head><body>
  <div>
    <h3 class="centered"><span class="kt color_blue">第一回</span></h3>
    <h2 class="centered">甄士隐梦幻识通灵 贾雨村风尘怀闺秀</h2><br/>
    <p>  列位看官,你道此书从何而来?<a id="navto_2_a" href="part0005.xhtml#lnkback_2_a"><sup></sup></a>说起根由虽近荒唐,<span class="small kt red"><img alt="甲戌本" class="font_patch" src="../Images/image00842.gif"/><img alt="侧批" class="font_patch" src="../Images/image00851.gif"/>自占地步。◇自首荒唐,妙!</span>细谙则深有趣味。待在下将此来历注明,方使阅者了然不惑。</p>
    <p>  <span class="small"><a id="lnkback_1_a" href="part0005.xhtml#navto_1_a"></a>以上文字见于庚、戚、蒙、列、辰、舒、杨诸本,其中甲辰本为回前批,馀本均为正文。此段与甲戌本凡例第五条略同,玩其文意应非正文,现作为回前批处理。</span></p>
    <p>  <span class="small"><a id="lnkback_2_a" href="part0005.xhtml#navto_2_a"></a>底本正文从此开始。按:本书第一至八回、第十三至十六回、第二十五至二十八回以甲戌本为底本,第六十四回、第六十七回以列藏本为底本,其馀部分以庚辰本为底本,下文所称底本均以此为据,不另注。</span></p>
     </div>
</body></html>

其中<a id="navto_2_a" href="part0005.xhtml#lnkback_2_a"><sup>②</sup></a>中的a标签,id属性不包含文件名信息,可能与其他文件中的a标签的id重复,href标签则包含文件名信息“part0005.xhtml”,合并为pdf文件后,文件“part0005.xhtml”不再存在,该链接失效。

为了避免上述问题,可以考虑将epub中所有网页文件合并为一个网页文件,a标签的id属性添加文件名信息改成形如“part0004_lnkback_2_a”的形式以避免出现重复idhref属性则改成形如“#part0004_lnkback_2_a”的形式直接链接到本文件中的锚点。

要实现上述目标,可以利用BeautifulSoup读入相关文件进行操作。示例代码如下:

import copy,re
from bs4 import BeautifulSoup
# merg_html保存所有网页文件合并后的html
merg_html = BeautifulSoup()
# 这里示例合并两个html字符串,实际操作时应该指定网页文件路径并遍历全部网页文件
for i in range(2): # 用遍历字符串模拟遍历网页文件
 if i==0:
  soup = BeautifulSoup(html1, 'lxml')
 else:
  soup = BeautifulSoup(html2, 'lxml')

 #修改注释部分链接属性,查找既有id属性又有href属性的a标签
 a_s=soup.find_all(name='a',attrs={'id':True,'href':True})
 # 这里根据给出html字符串的特征进行处理
 if len(a_s)>0:
   for a in a_s:
    f_name = a['href'].split('.')[0] # 从href属性中取得主文件名
    anchor_name = a['href'].split('#')[-1]  # 取得链接目标的锚点名                
    a['id'] = f_name + '_' + a['id'] # 将id前面加上主文件名
    a['href'] = '#' + f_name + '_' + anchor_name # 修改链接目标
    
 #修改目录部分链接属性
 a_s=soup.find_all(name='a',attrs={'id':False,'href':True})
 if len(a_s)>0:
   for a in a_s:
    f_name = a['href'].split('.')[0]
    a['id'] = 'nav_' + f_name
    a['href'] = '#' + f_name
       
 #回目编号处插入链接,title取得回目编号所在标签,从html2中可以看出特征 
 title = soup.find(name='span',text=re.compile('^第[一二三四五六七八九十]{1,5}回$'))                
 if title:
    s = 'part0005' # 这里用一个固定字符串示例,实际操作中应该在遍历文件时取得主文件名
    a = soup.new_tag('a')
    a['id'] = s
    a['href'] = '#nav_{}'.format(s)
    a.string = title.text.strip()
    title.string = '' # 清除回目编号中原有内容
    title.append(a)
    
 #将后续html文件字符串中body标签的内容全部复制到第一个文件中   
 if not merg_html.body: 
    merg_html = copy.copy(soup)
 else: 
    merg_html.body.append(copy.copy(soup.body))

data = merg_html.prettify()
# 删除字符串中多余的<body>标签和</body>标签,用[\s\S]代表所有字符,re.M指定多行模式
data = re.sub('</body>[\s\S]*?<body[\s\S]*?>','',data,flags=re.M)
data = re.sub('</body>[\s\S]*?</body[\s\S]*?>','</body>',data,flags=re.M)

print(data)

上述代码中的html1html2分别是本文开头的两个html文件内容。运行后合并html字符串内容如下:

<?xml version="1.0" encoding="utf-8" standalone="no"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xml:lang="zh-cn" xmlns="http://www.w3.org/1999/xhtml">
 <head>
  <title>
   目 录
  </title>
  <link href="../Styles/style0001.css" rel="stylesheet" type="text/css"/>
 </head>
 <body>
  <div>
   <h2>
    目 录
   </h2>
   <br/>
   <h4>
    <a href="#part0004" id="nav_part0004">
     脂砚斋重评石头记 凡例
    </a>
   </h4>
   <br/>
   <h4>
    <a href="#part0005" id="nav_part0005">
     第一回 甄士隐梦幻识通灵 贾雨村风尘怀闺秀
    </a>
   </h4>
   <h4>
    <a href="#part0006" id="nav_part0006">
     第二回 贾夫人仙逝扬州城 冷子兴演说荣国府
    </a>
   </h4>
  </div>
  <div>
   <div>
    <h3 class="centered">
     <span class="kt color_blue">
      <a href="#nav_part0005" id="part0005">
       第一回
      </a>
     </span>
    </h3>
    <h2 class="centered"> 甄士隐梦幻识通灵 贾雨村风尘怀闺秀  </h2>
    <br/>
    <p> 列位看官,你道此书从何而来?
     <a href="#part0005_lnkback_2_a" id="part0005_navto_2_a">
      <sup></sup> </a>  说起根由虽近荒唐, <span class="small kt red">
      <img alt="甲戌本" class="font_patch" src="../Images/image00842.gif"/>
      <img alt="侧批" class="font_patch" src="../Images/image00851.gif"/>
      自占地步。◇自首荒唐,妙!  </span> 细谙则深有趣味。待在下将此来历注明,方使阅者了然不惑。 </p>
    <p> <span class="small">
      <a href="#part0005_navto_1_a" id="part0005_lnkback_1_a"></a>
      以上文字见于庚、戚、蒙、列、辰、舒、杨诸本,其中甲辰本为回前批,馀本均为正文。此段与甲戌本凡例第五条略同,玩其文 意应非正文,现作为回前批处理。
     </span>
    </p>
    <p>
     <span class="small">
      <a href="#part0005_navto_2_a" id="part0005_lnkback_2_a"></a>
      底本正文从此开始。按:本书第一至八回、第十三至十六回、第二十五至二十八回以甲戌本为底本,第六十四回、第六十七回以 列藏本为底本,其馀部分以庚辰本为底本,下文所称底本均以此为据,不另注。
     </span>
    </p>
   </div>
  </div>
 </body>
</html>

可以看到两个html文件字符串已经合并成一个单一的html文件字符串,且相关链接已修正:
​​​​在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yivifu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值