• 使用htmldom分析HTML代码


    使用语言是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 name link pitch raised signal oined employee stage location ') 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 %s %s %s %s %s %s %s %s %s ' % 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则会丢失。

  • 相关阅读:
    PHP Socket 编程详解
    PHPWord生成word实现table合并(colspan和rowspan)
    PhpExcel中文帮助手册|PhpExcel使用方法
    js限制input标签中只能输入中文
    如何巧用.htaccess设置网站的压缩与缓存
    Linux xargs命令
    PHP加密解密类
    2014 年10个最佳的PHP图像操作库
    学习swoft的第二天_注解
    学习swoft的第一天
  • 原文地址:https://www.cnblogs.com/endoresu/p/7928250.html
Copyright © 2020-2023  润新知