htmlParser.py 6.9 KB
Newer Older
H
hjdhnx 已提交
1 2 3 4 5
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# File  : htmlParser.py
# Author: DaShenHan&道长-----先苦后甜,任凭晚风拂柳颜------
# Date  : 2022/8/25
H
hjdhnx 已提交
6
import json
H
hjdhnx 已提交
7 8

from pyquery import PyQuery as pq
9
from lxml import etree
H
hjdhnx 已提交
10
from urllib.parse import urljoin
H
hjdhnx 已提交
11
import re
H
hjdhnx 已提交
12
from jsonpath import jsonpath
H
hjdhnx 已提交
13 14 15 16 17

class jsoup:
    def __init__(self,MY_URL=''):
        self.MY_URL = MY_URL

H
hjdhnx 已提交
18
    def test(self, text:str, string:str):
H
hjdhnx 已提交
19 20 21 22
        searchObj = re.search(rf'{text}', string, re.M | re.I)
        test_ret = True if searchObj else False
        return test_ret

H
hjdhnx 已提交
23
    def pdfh(self,html,parse:str,add_url=False):
H
hjdhnx 已提交
24 25
        if not parse:
            return ''
H
hjdhnx 已提交
26
        doc = pq(html)
H
hjdhnx 已提交
27 28 29 30 31
        if parse == 'body&&Text' or parse == 'Text':
            text = doc.text()
            return text
        elif parse == 'body&&Html' or parse == 'Html':
            return doc.html()
H
hjdhnx 已提交
32 33
        option = None
        if parse.find('&&') > -1:
H
hjdhnx 已提交
34 35 36
            option = parse.split('&&')[-1]
            parse = parse.split('&&')[:-1]  # 如果只有一个&& 取的就直接是0
            if len(parse) > 1:  # 如果不大于1可能就是option操作,不需要拼eq
H
hjdhnx 已提交
37
                parse = ' '.join([i if self.test(':eq|:lt|:gt|#',i) else f'{i}:eq(0)' for i in parse])
H
hjdhnx 已提交
38
            else:
H
hjdhnx 已提交
39
                parse = parse[0] if self.test(':eq|:lt|:gt|#',parse[0]) else f'{parse[0]}:eq(0)'
H
hjdhnx 已提交
40
        # FIXME 暂时不支持jsonpath那样的|| 分割取或属性
H
hjdhnx 已提交
41
        if option:
H
hjdhnx 已提交
42
            # print(f'parse:{parse}=>(option:{option})')
H
hjdhnx 已提交
43 44 45 46 47
            if ':eq(-1)' in parse:
                # 处理 eq(-1)的情况,兼容性差,暂时只支持一层eq
                ret = doc(parse.replace(':eq(-1)','')).eq(-1)
            else:
                ret = doc(parse)
H
hjdhnx 已提交
48
            # print(html)
H
hjdhnx 已提交
49
            # FIXME 解析出来有多个的情况应该自动取第一个
H
hjdhnx 已提交
50 51 52 53 54
            if option == 'Text':
                ret = ret.text()
            elif option == 'Html':
                ret = ret.html()
            else:
H
hjdhnx 已提交
55
                ret = ret.attr(option) or ''
H
hjdhnx 已提交
56 57 58 59 60
                if option.lower().find('style')>-1 and ret.find('url(')>-1:
                    try:
                        ret = re.search('url\((.*?)\)',ret,re.M|re.S).groups()[0]
                    except:
                        pass
H
hjdhnx 已提交
61 62 63
                pd_list = 'url|src|href|data-original|data-src|data-play'.split('|')
                # pd_list = 'url|src|href|data-original|data-src'.split('|')
                if ret and add_url and option in pd_list:
H
hjdhnx 已提交
64 65 66 67 68
                    if 'http' in ret:
                        ret = ret[ret.find('http'):]
                    else:
                        ret = urljoin(self.MY_URL,ret)
                    # print(ret)
H
hjdhnx 已提交
69
        else:
H
hjdhnx 已提交
70 71 72 73 74 75
            # ret = doc(parse+':first')
            ret = doc(parse) # 由于是生成器,直接转str就能拿到第一条数据,不需要next
            # ret = ret.next()  # 取第一条数据
            # ret = doc(parse) # 下面注释的写法不对的
            # ret = ret.find(':first')
            # ret = ret.children(':first')
76 77 78
            # print(parse)
            # ret = str(ret)
            ret = ret.outerHtml()
H
hjdhnx 已提交
79 80
        return ret

H
hjdhnx 已提交
81
    def pdfa(self,html,parse:str):
82 83
        # 看官方文档才能解决这个问题!!!
        # https://pyquery.readthedocs.io/en/latest/api.html
H
hjdhnx 已提交
84 85
        if not parse:
            return []
H
hjdhnx 已提交
86 87 88 89 90
        if parse.find('&&') > -1:
            parse = parse.split('&&')  # 带&&的重新拼接
            # print(f"{parse[0]},{self.test(':eq|:lt|:gt', parse[0])}")
            parse = ' '.join([parse[i] if self.test(':eq|:lt|:gt', parse[i]) or i>=len(parse)-1 else f'{parse[i]}:eq(0)' for i in range(len(parse))])
        # print(f'pdfa:{parse}')
H
hjdhnx 已提交
91
        doc = pq(html)
92 93 94 95
        result = doc(parse)
        # 节点转字符串
        # print(str(etree.tostring(result[0], pretty_print=True), 'utf-8'))
        # res = [item for item in result.items()]
H
hjdhnx 已提交
96
        # print(res)
97 98 99
        res = [item.outerHtml() for item in result.items()] #  这个才是对的!!str() item str(etree.tostring 统统错误
        # res = [str(item) for item in result.items()]
        # res = [str(etree.tostring(item, pretty_print=True), 'utf-8') for item in result]
100
        # print(len(res),res)
101
        # print('pdfa执行结果数:',len(res))
102
        return res
H
hjdhnx 已提交
103

H
hjdhnx 已提交
104
    def pd(self,html,parse:str):
H
hjdhnx 已提交
105 106
        return self.pdfh(html,parse,True)

H
hjdhnx 已提交
107
    def pq(self,html:str):
H
hjdhnx 已提交
108 109
        return pq(html)

H
hjdhnx 已提交
110 111 112 113 114 115 116 117 118 119 120 121 122
    def pjfh(self,html,parse:str,add_url=False):
        if not parse:
            return ''
        if isinstance(html,str):
            # print(html)
            try:
               html = json.loads(html)
               # html = eval(html)
            except:
                print('字符串转json失败')
                return ''
        if not parse.startswith('$.'):
            parse = f'$.{parse}'
H
hjdhnx 已提交
123 124 125 126 127 128 129 130 131 132 133
        ret = ''
        for ps in parse.split('||'):
            ret = jsonpath(html,ps)
            if isinstance(ret,list):
                ret = str(ret[0]) if ret[0] else ''
            else:
                ret = str(ret) if ret else ''
            if add_url and ret:
                ret = urljoin(self.MY_URL, ret)
            if ret:
                break
H
hjdhnx 已提交
134
        # print(ret)
H
hjdhnx 已提交
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
        return ret

    def pj(self, html, parse:str):
        return self.pjfh(html, parse, True)

    def pjfa(self,html,parse:str):
        if not parse:
            return []
        if isinstance(html,str):
            try:
               html = json.loads(html)
            except:
                return ''
        if not parse.startswith('$.'):
            parse = f'$.{parse}'
H
hjdhnx 已提交
150
        # print(html)
H
hjdhnx 已提交
151 152 153 154 155 156 157 158 159 160 161
        # print(parse)
        ret = jsonpath(html,parse)
        # print(ret)
        # print(type(ret))
        # print(type(ret[0]))
        # print(len(ret))
        if isinstance(ret,list) and isinstance(ret[0],list) and len(ret) == 1:
            # print('自动解包')
            ret  = ret[0] # 自动解包
        return ret or []

H
hjdhnx 已提交
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
if __name__ == '__main__':
    import requests
    from parsel import Selector
    url = 'http://360yy.cn'
    jsp = jsoup(url)
    def pdfa2(html,parse):
        if not parse:
            return []
        if parse.find('&&') > -1:
            parse = parse.split('&&')  # 带&&的重新拼接
            # print(f"{parse[0]},{self.test(':eq|:lt|:gt', parse[0])}")
            # parse = ' '.join([parse[i] if self.test(':eq|:lt|:gt', parse[i]) or i>=len(parse)-1 else f'{parse[i]}:eq(0)' for i in range(len(parse))])
            parse = ' '.join([parse[i] if jsoup().test(':eq|:lt|:gt', parse[i]) or i>=len(parse)-1 else f'{parse[i]}:nth-child(1)' for i in range(len(parse))])
        # print(f'pdfa:{parse}')
        selector = Selector(text=html)
        print(parse)
        items = selector.css(parse)
        return [str(item) for item in items]
    r = requests.get(url)
    html = r.text
    # parsel 不好用啊,很难实现封装pdfa之类的函数
    items = pdfa2(html,'.fed-pops-navbar&&ul.fed-part-rows&&a')
    print(items)