使用语言是Python 3.5。开发环境是Windows。

在使用HTMLParser库的时候,发现它不能正确的解析多重div元素嵌套的情况,因为这些div元素中又包含了a元素等其它元素。

这似乎是一个长期以来都没解决的BUG:

https://sourceforge.net/p/nekohtml/bugs/98/
http://jericho.htmlparser.net//docs/javadoc/net/htmlparser/jericho/StartTag.html

于是我寻找一个新库,希望它能像javascript那样,能从html代码构建一个dom对象模型。但没找到完美的,只找到这个库:

http://thehtmldom.sourceforge.net/

我的源代码下载的方法是这样的:

1. 首先在火狐中打开URL;

2. Ctrl+Shift+C打开“DOM和样式查看器”;

3. 选中顶部html元素,右键选择复制outerHTML(HTML外面O);

4. 找个文本编辑器粘贴,保存为utf-8编码。

然后直接上我的代码,记忆力不好,以后便于我直接复制:

#!/usr/bin/python
# -*- coding: <encoding name> -*- from htmldom import htmldom url = 'file:///C:/Users/Microsoft/Desktop/analyse/ict_in_companies.html'
dom = htmldom.HtmlDom(url).createDom() def returndict(estartup):
if not isinstance(estartup, htmldom.HtmlNodeList):
return None
_returndict = {'market': '', 'name': '', 'link': '', 'pitch': '', 'raised': '', 'signal': '', 'joined': '', 'employee': '', 'stage': '', 'location': ''}
try:
etext = estartup.children('div[class~=company]').first().children().first().children('div[class=text]').first()
ename = etext.children('div[class=name]').first()
elink = ename.children('a[class=startup-link]').first()
epitch = etext.children('div[class=pitch]').first()
except BaseException as err:
print('**** ERROR There is something wrong! ****')
print(err)
else:
_returndict['name'] = ename.text().rstrip()
_returndict['pitch'] = epitch.text().rstrip()
_returndict['link'] = elink.attr('href').rstrip()
try:
ejoined = estartup.children('div[class~=joined]').first().children('div[class=value]').first()
elocation = estartup.children('div[class~=location]').first().children('div[class=value]').first()
emarket = estartup.children('div[class~=market]').first().children('div[class=value]').first()
eemployee = estartup.children('div[class~=company_size]').first().children('div[class=value]').first()
estage = estartup.children('div[class~=stage]').first().children('div[class=value]').first()
eraised = estartup.children('div[class~=raised]').first().children('div[class=value]').first()
esignal = estartup.children('div[class~=signal]').first().children('div[class=value]').first().children('img').first()
except BaseException as err:
print('**** ERROR There is something wrong! ****')
print(err)
else:
_returndict['joined'] = ejoined.text().rstrip()
_returndict['location'] = elocation.text().rstrip()
_returndict['market'] = emarket.text().rstrip()
_returndict['employee'] = "'%s" % eemployee.text().rstrip()
_returndict['stage'] = estage.text().rstrip()
_returndict['raised'] = eraised.text().rstrip()
_returndict['signal'] = esignal.attr('src')[37:38].rstrip()
return _returndict def returngbk(original):
    return original.encode('gbk', 'ignore')
    #return original.encode('utf-8') ecompanies = dom.find('div[class~=frw44]')
lcompanies = []
for ecompany in ecompanies:
estartup = ecompany.children(selector='div[class~=startup]', all_children=False).first()
dcompany = returndict(estartup)
if not dcompany: continue
print('index: %d' % len(lcompanies))
lcompanies.append(dcompany) output = open('C:/Users/Microsoft/Desktop/a.csv', 'wb')
output.write(b'market\tname\tlink\tpitch\traised\tsignal\toined\temployee\tstage\tlocation\n')
for i in range(len(lcompanies)):
print('Index: %d' % i)
tcompany = (returngbk(lcompanies[i]['market']),
returngbk(lcompanies[i]['name']),
returngbk(lcompanies[i]['link']),
returngbk(lcompanies[i]['pitch']),
returngbk(lcompanies[i]['raised']),
returngbk(lcompanies[i]['signal']),
returngbk(lcompanies[i]['joined']),
returngbk(lcompanies[i]['employee']),
returngbk(lcompanies[i]['stage']),
returngbk(lcompanies[i]['location']))
data = b'%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n' % tcompany
output.write(data)
output.close

htmldom 2.0这个库有个特点,每次通过一定方式(如find方法、children方法、for...in语句等)返回的对象,始终都是HtmlNodeList类型的,这给使用这个库造成了一些歧义。

例如,children方法应该返回的是当前元素p的集合cs,用for...in语句遍历得到的c才是子元素本身,但p(单个元素)、cs(集合)、c(单个元素)都是HtmlNodeList类型的对象,意味着它们的方法属性都是相同的。

如果cs中只有一个c,c.attr(...)可以成功,而cs.attr(...)是不能成功的。如果想引用c,可以使用cs.first(),即c is cs.first()。不要把cs和c搞混了。

还有,虽然手册中children方法能够通过传参指定是否返回递归的子元素,但实际上传参与否,都只能返回下一级的子元素。所以就当作没有这个参数吧。

最后,类似于dom.find('div[class=classname]')这样的用法,classname是不能有空格或者其它特殊字符的,如果有只能使用dom.find('div[class~=assna]')这样的用法。

此次在Windows平台下使用open函数写入文件发现一些问题:网页中的某些字符无法转换成GBK编码字节并写入文件,代码运行后报错终止。

那么,为什么要转换成GBK编码写入文件,是我的代码中这么做了吗?答案是我的代码没这么做,但操作系统默认使用GBK编码保存文本文件,所以有这一转换。

由于html源代码是utf-8格式,当中有一部分unicode字符集的内容无法映射至gbk字符集,因此报错。解决方法是代码里主动转换,使用s.encode('gbk', 'ignore'),s是原字符串对象,ignore是必加参数,表示不能转换的则忽略。type(s.encode('gbk', 'ignore'))可知其类型是bytes类型,即__repr__()是b'...'。既然已经是bytes类型,直接以二进制的方式写入文本,所以open函数使用wb参数。

当然,直接s.encode('utf-8')结合open(path, 'wb'),也是不会报错的,但是在Windows平台下,一般软件显示的字符还是GBK编码,导致UTF-8编码的字符在显示时会有问题,需要手动设置为UTF-8解决这个问题。

附上excel中打开utf-8编码的.csv文件不乱码的方法:

https://jingyan.baidu.com/article/48a4205705c098a925250455.html

发现这个方法更好用,尤其是一些特殊字符,如欧元符号等可以保留,若是转换成GBK则会丢失。

使用htmldom分析HTML代码的更多相关文章

  1. 洛谷P1345 [USACO5.4]奶牛的电信Telecowmunication【最小割】分析+题解代码

    洛谷P1345 [USACO5.4]奶牛的电信Telecowmunication[最小割]分析+题解代码 题目描述 农夫约翰的奶牛们喜欢通过电邮保持联系,于是她们建立了一个奶牛电脑网络,以便互相交流. ...

  2. 洛谷 P2194 HXY烧情侣【Tarjan缩点】 分析+题解代码

    洛谷 P2194 HXY烧情侣[Tarjan缩点] 分析+题解代码 题目描述: 众所周知,HXY已经加入了FFF团.现在她要开始喜(sang)闻(xin)乐(bing)见(kuang)地烧情侣了.这里 ...

  3. 洛谷P2832 行路难 分析+题解代码【玄学最短路】

    洛谷P2832 行路难 分析+题解代码[玄学最短路] 题目背景: 小X来到了山区,领略山林之乐.在他乐以忘忧之时,他突然发现,开学迫在眉睫 题目描述: 山区有n座山.山之间有m条羊肠小道,每条连接两座 ...

  4. 洛谷P1783 海滩防御 分析+题解代码

    洛谷P1783 海滩防御 分析+题解代码 题目描述: WLP同学最近迷上了一款网络联机对战游戏(终于知道为毛JOHNKRAM每天刷洛谷效率那么低了),但是他却为了这个游戏很苦恼,因为他在海边的造船厂和 ...

  5. 洛谷P1854 花店橱窗布置 分析+题解代码

    洛谷P1854 花店橱窗布置 分析+题解代码 蒟蒻的第一道提高+/省选-,纪念一下. 题目描述: 某花店现有F束花,每一束花的品种都不一样,同时至少有同样数量的花瓶,被按顺序摆成一行,花瓶的位置是固定 ...

  6. 《linux内核分析》作业一:分析汇编代码

    通过汇编一个简单的C程序,分析汇编代码理解计算机是如何工作的(王海宁) 姓名:王海宁                             学号:20135103 课程:<Linux内核分析& ...

  7. 通过汇编一个简单的C程序,分析汇编代码理解计算机是如何工作的

    秦鼎涛  <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 实验一 通过汇编一个简单的C程序,分析汇编代码 ...

  8. Java NIO原理 图文分析及代码实现

    Java NIO原理图文分析及代码实现 前言:  最近在分析hadoop的RPC(Remote Procedure Call Protocol ,远程过程调用协议,它是一种通过网络从远程计算机程序上请 ...

  9. 通过反汇编一个简单的C程序,分析汇编代码理解计算机是如何工作的

    实验一:通过反汇编一个简单的C程序,分析汇编代码理解计算机是如何工作的 学号:20135114 姓名:王朝宪 注: 原创作品转载请注明出处   <Linux内核分析>MOOC课程http: ...

随机推荐

  1. 【剑指offer】面试题 29. 顺时针打印矩阵

    面试题 29. 顺时针打印矩阵 题目描述 题目:输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ...

  2. java网络通信:HTTP协议 之 Sessions与Cookies

    通过前一篇博客的讲解,我们大体知道了HTTP协议是什么,它有什么组成,以及它的工作原理,那么在HTTP的很多特点中,有一点叫做,无状态,就HTTP是一个无状态的协议,如果需要前面的信息用于处理后边的请 ...

  3. php定位并且获取天气信息

    /** *获取天气预报信息 **/ header("Content-type: text/html; charset=utf-8"); class getWeather{ priv ...

  4. scrapy抓取拉勾网职位信息(一)——scrapy初识及lagou爬虫项目建立

    本次以scrapy抓取拉勾网职位信息作为scrapy学习的一个实战演练 python版本:3.7.1 框架:scrapy(pip直接安装可能会报错,如果是vc++环境不满足,建议直接安装一个visua ...

  5. 洛谷——P1478 陶陶摘苹果(升级版)

    题目描述 又是一年秋季时,陶陶家的苹果树结了n个果子.陶陶又跑去摘苹果,这次她有一个a公分的椅子.当他手够不着时,他会站到椅子上再试试. 这次与NOIp2005普及组第一题不同的是:陶陶之前搬凳子,力 ...

  6. 解决vscode按下ctrl+S的时候自动格式化

    按下ctrl+S的时候自动格式化 为什么需要这种操作? 优点: 保存的时候格式化,让我们的代码自动格式化,减少人工调整. 缺点: 有一些打好包的JS有时候修改一下,但不需要格式化,因为打好包就是要压缩 ...

  7. Visual Studio找不到adb.exe错误解决

    Visual Studio找不到adb.exe错误解决 错误信息:Cannot find adb.exe in specified SDK path.出现这种情况,是因为没有安装Android SDK ...

  8. [SRM686]CyclesNumber

    题意:求$n$个数的所有排列形成的轮换个数的$m$次方之和 我以前只知道这是GDKOI的题,今天在ckw博客上发现它是TC题...原题真是哪里都有... 就是求$\sum\limits_{i=1}^n ...

  9. BZOJ 4029: [HEOI2015]定价 贪心

    4029: [HEOI2015]定价 题目连接: http://www.lydsy.com/JudgeOnline/problem.php?id=4029 Description 在市场上有很多商品的 ...

  10. Ubuntu 16.04通过NetworkManager(GUI)配置网桥

    说明:配置好网桥之后一定要重启,不然不生效.这个是Desktop版GUI设置的问题.Server版不会. 配置: 参考: http://www.jb51.net/LINUXjishu/333778.h ...