精华内容
下载资源
问答
  • 通过java代码json格式数据进行解析

    千次阅读 2015-02-12 16:33:14
    通过java代码json格式数据进行解析

           面对一个json格式的数据,我们如何把其中的数据解析出来呢;

          1.首先我们先要确认数据格式是否符合json格式的数据要求,可以在线json数据解析:   http://write.blog.csdn.net/postedit

            (Results 是 Valid JSON 表示格式正确)

         2.json实例:大括号括起来的是JSONObject,以方括号括起来的是jsonArray;

          

          3.解析

           JSONObject obj = new JSONObject(jsonStr);    //jsonStr为2中的json数据

           int total = (Integer)obj.getInteger("total");

          JSONArray array = jsonObj.getJSONArray("root");

           for(int i = 0; i < array.length(); i++){

                          JSONObject obj1 =  array.getJSONObject(i);

                          String name = obj1.getString("name");

                           int age = obj1.getInteger("age");         

            }

    展开全文
  • 看到本文,相信以后你任何形式额字体反爬都能见招拆招。 文章目录深度剖析自定义字体解析自定义字体的介绍Python加载页面解析顶部导航栏分类和地点列表解析字体对应css的下载URL解析css获取自定义字体的URL

    大家好,我的小小明。
    前面我在《Python处理超强反爬(TSec防火墙+CSS图片背景偏移定位)》一文中讲解如何解析css图片背景偏移的数据,并通过图像识别提取文字。

    本文将带你解析各种形式自定义字体,绘制点阵图,并通过图像识别提取出关系列表,最终校对后构建正确的对应关系,最终获取到正确的数据。

    看到本文,相信以后你对任何形式额字体反爬都能见招拆招。

    深度剖析自定义字体解析

    自定义字体的介绍

    首先,我们必须要清楚自定义字体与普通字体的区别,自定义字体定义了一些特殊的Unicode编码对应的点阵图数据,而普通字体只是定义标准编码的显示形式,所以普通字体渲染的数据可以直接复制出正确的文本,而自定义字体只能复制到对应的Unicode编码。

    那么游览器如何显示出对应的字符呢?那是因为游览器会根据自定义字体的对应关系,渲染对应的点阵图进行显示。

    下面我们以某团购网站为例进行演示。

    这次我分析的页面是深圳休闲娱乐

    image-20210802161659847

    image-20210802162026699

    可以看到自定义字体都存在于svgmtsi标签中,不同的class属性也对应了不同自定义字体文件。

    如果我们取消所有的自定义字体的加载,可以看到网页上对应的位置都会出现乱码:

    image-20210802162640117

    从上图也可以看到,产生自定义字体的位置完全是随机的。

    对于这种情况,我们最好使用可以修改HTML DOM树的库来维持节点的相对顺序,我选择了BeautifulSoup这个库,可惜只支持css选择器。

    不过也好,早期我学编程用Java玩小爬虫的时候就更喜欢css选择器,正好可以找回久违的感觉。

    接下来我们一步步分析页面,首先用python读取页面数据:

    Python加载页面

    import requests
    
    headers = {
        "Connection": "keep-alive",
        "Cache-Control": "max-age=0",
        "Upgrade-Insecure-Requests": "1",
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36",
        "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
        "Accept-Language": "zh-CN,zh;q=0.9"
    }
    session = requests.Session()
    session.headers = headers
    res = session.get("http://www.dianping.com/shenzhen/ch30")
    

    下面我们使用BeautifulSoup解析下载的页面,构建DOM树:

    from bs4 import BeautifulSoup
    
    soup = BeautifulSoup(res.text, 'html5lib')
    

    关于BeautifulSoup可以查看官方文档:

    (上面两个链接内容一样,目录形式有区别)

    解析顶部导航栏分类和地点列表

    由于现在该团购网站翻第二页就要求登录,咱们也没有打算真的要爬它。所以我通过多下载几个分类链接,来模拟批量下载的效果。

    下面准备解析出下面这些对应的标题:

    image-20210802165248267

    通过xpath查询工具获取到xpath后,就可以转换为css选择器。

    分类列表:

    # //div[@id='classfy']/a/span
    type_list = []
    for a_tag in soup.select("div#classfy > a"):
        type_list.append((a_tag.span.text, a_tag['href']))
    type_list
    
    [('按摩/足疗', 'http://www.dianping.com/shenzhen/ch30/g141'),
     ('KTV', 'http://www.dianping.com/shenzhen/ch30/g135'),
     ('洗浴/汗蒸', 'http://www.dianping.com/shenzhen/ch30/g140'),
     ('酒吧', 'http://www.dianping.com/shenzhen/ch30/g133'),
     ('运动健身', 'http://www.dianping.com/shenzhen/ch30/g2636'),
     ('茶馆', 'http://www.dianping.com/shenzhen/ch30/g134'),
     ('密室', 'http://www.dianping.com/shenzhen/ch30/g2754'),
     ('团建拓展', 'http://www.dianping.com/shenzhen/ch30/g34089'),
     ('采摘/农家乐', 'http://www.dianping.com/shenzhen/ch30/g20038'),
     ('剧本杀', 'http://www.dianping.com/shenzhen/ch30/g50035'),
     ('游戏厅', 'http://www.dianping.com/shenzhen/ch30/g137'),
     ('DIY手工坊', 'http://www.dianping.com/shenzhen/ch30/g144'),
     ('私人影院', 'http://www.dianping.com/shenzhen/ch30/g20041'),
     ('轰趴馆', 'http://www.dianping.com/shenzhen/ch30/g20040'),
     ('网吧/电竞', 'http://www.dianping.com/shenzhen/ch30/g20042'),
     ('VR', 'http://www.dianping.com/shenzhen/ch30/g33857'),
     ('桌面游戏', 'http://www.dianping.com/shenzhen/ch30/g6694'),
     ('棋牌室', 'http://www.dianping.com/shenzhen/ch30/g32732'),
     ('文化艺术', 'http://www.dianping.com/shenzhen/ch30/g142'),
     ('新奇体验', 'http://www.dianping.com/shenzhen/ch30/g34090')]
    

    地点列表:

    # //div[@id='region-nav']/a/span
    area_list = []
    for a_tag in soup.select("div#region-nav > a"):
        area_list.append((a_tag.span.text, a_tag['href']))
    area_list
    
    [('福田区', 'http://www.dianping.com/shenzhen/ch30/r29'),
     ('南山区', 'http://www.dianping.com/shenzhen/ch30/r31'),
     ('罗湖区', 'http://www.dianping.com/shenzhen/ch30/r30'),
     ('盐田区', 'http://www.dianping.com/shenzhen/ch30/r32'),
     ('龙华区', 'http://www.dianping.com/shenzhen/ch30/r12033'),
     ('龙岗区', 'http://www.dianping.com/shenzhen/ch30/r34'),
     ('宝安区', 'http://www.dianping.com/shenzhen/ch30/r33'),
     ('坪山区', 'http://www.dianping.com/shenzhen/ch30/r12035'),
     ('光明区', 'http://www.dianping.com/shenzhen/ch30/r89951'),
     ('南澳大鹏新区', 'http://www.dianping.com/shenzhen/ch30/r12036')]
    

    解析字体对应css的下载URL

    经观察可以发现,定义自定义字体的css文件在链接带有svgtextcss关键字的url中:

    image-20210802170838134

    我们可以从所有的定义css样式的链接中找到含有svgtextcss关键字的链接:

    from urllib import parse
    
    def getUrlFromNode(nodes, tag):
        for node in nodes:
            url = node['href']
            if url.find(tag) != -1:
                return parse.urljoin(base_url, url)
    
    
    def get_css_url(soup):
        css_url = getUrlFromNode(soup.select(
            "head > link[rel=stylesheet]"), "svgtextcss")
        return css_url
    
    
    css_url = get_css_url(soup)
    css_url
    
    'http://s3plus.meituan.net/v1/mss_0a06a471f9514fc79c981b5466f56b91/svgtextcss/18379bbeb1f5bf54c52bb1d8b71d4fb1.css'
    

    解析css获取自定义字体的URL

    格式化定义字体的css文件:

    image-20210802172030864

    可以看到,class定义了使用的字体名称,font-face定义了每个字体名称对应的字体文件。

    虽然现在我们可以看到规律每个class就是加了一个PingFangSC-Regular-的前缀作为字体名称,但是我们无法保证以后该网站依然会这样设计,为了保证以后在这个点上面不需要改代码,我们依然还是解析出每个class对应的font-family,再解析出每个font-family对应的多个字体URL,最终多个字体URL取后缀为.woff格式的URL,建立class属性到woff字体的映射关系。

    下面是完整代码:

    import re
    
    
    def get_url(urls, tag, only_First=True):
        urls = [parse.urljoin(base_url, url)
                for url in urls if tag is None or url.find(tag) != -1]
        if urls and only_First:
            return urls[0]
        return urls
    
    
    def parseCssFontUrl(css_url, tag=None, only_First=True):
        res = session.get(css_url)
        rule = {}
        font_face = {}
        for name, value in re.findall("([^{}]+){([^{}]+)}", res.text):
            name = name.strip()
            for row in value.split(";"):
                if row.find(":") == -1:
                    continue
                k, v = row.split(":")
                k, v = k.strip(), v.strip(' "\'')
                if name == "@font-face":
                    if k == "font-family":
                        font_name = v
                    elif k == "src":
                        font_face.setdefault(font_name, []).extend(
                            re.findall("url\(\"([^()]+)\"\)", v))
                else:
                    rule[name[1:]] = v
        font_urls = {}
        for class_name, tag_name in rule.items():
            font_urls[class_name] = get_url(font_face[tag_name], tag)
        return font_urls
    
    
    font_urls = parseCssFontUrl(css_url, ".woff", only_First=False)
    font_urls
    
    {'shopNum': 'http://s3plus.meituan.net/v1/mss_73a511b8f91f43d0bdae92584ea6330b/font/89e46c52.woff',
     'tagName': 'http://s3plus.meituan.net/v1/mss_73a511b8f91f43d0bdae92584ea6330b/font/f8536a55.woff',
     'reviewTag': 'http://s3plus.meituan.net/v1/mss_73a511b8f91f43d0bdae92584ea6330b/font/0373a060.woff',
     'address': 'http://s3plus.meituan.net/v1/mss_73a511b8f91f43d0bdae92584ea6330b/font/f8536a55.woff'}
    

    下载字体

    我们可以将上述四个字体都下载下来看看:

    def download_file(url, out_name=None):
        if out_name is None:
            out_name = url[url.rfind("/")+1:]
        with open(out_name, "wb") as f:
            f.write(session.get(url).content)
    
    for class_name, url in font_urls.items():
        download_file(url, f"{class_name}.woff")
    

    下载后得到4个字体文件:

    image-20210802173222464

    想要本地查看字体,我们可以通过FontCreator字体设计工具,百度一下可以直接搜索到下载链接。

    打开后:

    image-20210802174851524

    经过对比发现四个文件的点阵图顺序完全一致,不同的只是编码与点阵图的关系。

    建立自定义字体映射关系

    下面我们需要分析对于指定字体每个被定义的Unicode字符对应的真实字符。由于字体文件中存储的字符的点阵图,本质是图片而不是文本,所以我们无法复制出来。但我们可以考虑通过PIL加载自定义字体,然后将每个被定义的Unicode字符画出相应的点阵图,再进行图像识别,就可以获取相应的文本数据了。

    这里需要使用fontTools工具,可以直接使用pip安装。

    详见:https://github.com/fonttools/fonttools

    以class等于tagName的字体为例,先获取其被定义的Unicode字符列表:

    from fontTools.ttLib import TTFont
    
    tfont = TTFont("tagName.woff")
    # 去掉前2个扩展字符
    uni_list = tfont.getGlyphOrder()[2:]
    print(uni_list[:10], len(uni_list))
    
    ['uniec3e', 'unif3fc', 'uniea1f', 'unie7f7', 'unie258', 'unif5aa', 'unif48c', 'unif088', 'unif588', 'unif82e'] 601
    

    这里打印了前10个Unicode代码点,共有601个自定义字符。

    打印结果也与上面的截图中FontCreator字体设计工具查看的结果一致。

    使用PIL绘图工具,先绘制前5个代码点测试一下:

    from PIL import ImageFont, Image, ImageDraw
    
    font = ImageFont.truetype("tagName.woff", 20)
    for uchar in uni_list[:5]:
        unknown_char = f"\\u{uchar[3:]}".encode().decode("unicode_escape")
        im = Image.new(mode='RGB', size=(22, 20), color="white")
        draw = ImageDraw.Draw(im=im)
        draw.text(xy=(5, -5), text=unknown_char, fill=0, font=font)
        display(im)
    

    绘制结果:

    image-20210802181849772

    可以看到能够正确绘制出相应的点阵图。

    下面再测试每n个代码点为一组一起绘制,减少后面图像识别的次数(这里设置n=25,绘制5组):

    n = 25
    font = ImageFont.truetype("tagName.woff", 20)
    for i in range(0, 5*n, n):
        im = Image.new(mode='RGB', size=(20*n+10, 22), color="white")
        draw = ImageDraw.Draw(im=im)
        unknown_chars = "".join(uni_list[i:i + n]).replace("uni", "\\u")
        unknown_chars = unknown_chars.encode().decode("unicode_escape")
        draw.text(xy=(5, -4), text=unknown_chars, fill=0, font=font)
        display(im)
    

    绘制结果:

    image-20210802181925813

    封装一下,批量获取一个字体文件的全部图片对象:

    from fontTools.ttLib import TTFont
    from PIL import ImageFont, Image, ImageDraw
    
    
    def getCustomFontGroupImgs(font_file, uni_list=None, group_num=25):
        if uni_list is None:
            tfont = TTFont(font_file)
            uni_list = tfont.getGlyphOrder()[2:]
        imgs = []
        font = ImageFont.truetype(font_file, 20)
        for i in range(0, len(uni_list), group_num):
            im = Image.new(mode='RGB', size=(20*group_num+10, 22), color="white")
            draw = ImageDraw.Draw(im=im)
            unknown_chars = "".join(uni_list[i:i + group_num]).replace("uni", "\\u")
            unknown_chars = unknown_chars.encode().decode("unicode_escape")
            draw.text(xy=(5, -4), text=unknown_chars, fill=0, font=font)
            imgs.append(im)
        return imgs
    

    pytesseract默认不支持对中文的识别,需要较多的配置。这次我们直接使用一个最近比较流行的库叫带带弟弟orc来进行图像识别,一行命令即可安装:

    pip install ddddocr
    

    使用示例和参数可以查看:https://pypi.org/project/ddddocr/

    不过该库只支持传图片字节和base64编码,不支持直接传入图片对象,需要二次转换。

    可以定义一个将图片转字节的方法:

    from io import BytesIO
    
    def get_img_bytes(img):
        img_byte = BytesIO()
        im.save(img_byte, format='JPEG')  # format: PNG or JPEG
        return img_byte.getvalue()  # im对象转为二进制流
    

    然后就可以以如下形式进行批量识别:

    from ddddocr import DdddOcr
    
    imgs = getCustomFontGroupImgs('shopNum.woff', group_num=50)
    ocr = DdddOcr()
    result = []
    for im in imgs:
        display(im)
        text = ocr.classification(get_img_bytes(im))
        print(text)
        result.append(text)
    

    效果如下:

    image-20210802193638811

    整体来说准确率还是非常高的。

    我最终还是决定直接继承DdddOcr类,重写识别方法优化算法(以后再考虑自行开发图像识别类):

    from ddddocr import DdddOcr, np
    
    
    class OCR(DdddOcr):
        def __init__(self):
            super().__init__()
    
        def ocr(self, image):
            image = image.resize(
                (int(image.size[0] * (64 / image.size[1])), 64), Image.ANTIALIAS).convert('L')
            image = np.array(image).astype(np.float32)
            image = np.expand_dims(image, axis=0) / 255.
            image = (image - 0.5) / 0.5
            ort_inputs = {'input1': np.array([image])}
            ort_outs = self._DdddOcr__ort_session.run(None, ort_inputs)
            result = []
            last_item = 0
            for item in ort_outs[0][0]:
                if item == 0 or item == last_item:
                    continue
                result.append(self._DdddOcr__charset[item])
                last_item = item
            return ''.join(result)
    

    然后这样调用:

    imgs = getCustomFontGroupImgs('shopNum.woff', group_num=42)
    ocr = OCR()
    result = []
    for im in imgs:
        display(im)
        text = ocr.ocr(im)
        print(text)
        result.append(text)
    

    image-20210803100946648

    可以看到经过继承调整后的代码,识别准确率更高了些。

    最终我们人工校对修改后,得到如下字符集:

    words = '1234567890店中美家馆小车大市公酒行国品发电金心业商司超生装园场食有新限天面工服海华水房饰城乐汽香部利子老艺花专东肉菜学福饭人百餐茶务通味所山区门药银农龙停尚安广鑫一容动南具源兴鲜记时机烤文康信果阳理锅宝达地儿衣特产西批坊州牛佳化五米修爱北养卖建材三会鸡室红站德王光名丽油院堂烧江社合星货型村自科快便日民营和活童明器烟育宾精屋经居庄石顺林尔县手厅销用好客火雅盛体旅之鞋辣作粉包楼校鱼平彩上吧保永万物教吃设医正造丰健点汤网庆技斯洗料配汇木缘加麻联卫川泰色世方寓风幼羊烫来高厂兰阿贝皮全女拉成云维贸道术运都口博河瑞宏京际路祥青镇厨培力惠连马鸿钢训影甲助窗布富牌头四多妆吉苑沙恒隆春干饼氏里二管诚制售嘉长轩杂副清计黄讯太鸭号街交与叉附近层旁对巷栋环省桥湖段乡厦府铺内侧元购前幢滨处向座下澩凤港开关景泉塘放昌线湾政步宁解白田町溪十八古双胜本单同九迎第台玉锦底后七斜期武岭松角纪朝峰六振珠局岗洲横边济井办汉代临弄团外塔杨铁浦字年岛陵原梅进荣友虹央桂沿事津凯莲丁秀柳集紫旗张谷的是不了很还个也这我就在以可到错没去过感次要比觉看得说常真们但最喜哈么别位能较境非为欢然他挺着价那意种想出员两推做排实分间甜度起满给热完格荐喝等其再几只现朋候样直而买于般豆量选奶打每评少算又因情找些份置适什蛋师气你姐棒试总定啊足级整带虾如态且尝主话强当更板知己无酸让入啦式笑赞片酱差像提队走嫩才刚午接重串回晚微周值费性桌拍跟块调糕'
    

    字体文件中的Unicode代码点则与上述字符集字符一一对应。

    由于该网站所有的自定义字体的点阵图都是这个顺序,所以我们不再需要解析其他的字体文件获取这个字符列表。当然这个团购网站以后还打算变态到每个字体文件的点阵图顺序也随机,那我只能说,真狠。那到时候我再考虑升级自己的代码,因为我个人的目标就是没有我解析不了的数据。

    有了点阵图对应的字符集,咱们就可以轻松建立字体文件的映射关系:

    from fontTools.ttLib import TTFont
    
    font_data = TTFont("tagName.woff")
    uni_list = font_data.getGlyphOrder()[2:]
    font_map = dict(zip(map(lambda x: x[3:], uni_list), words))
    

    字体缓存器

    针对该团购网站,由于我们无法保证所有页面用这一个相同的css文件,所以我们需要建立一个css的URL到字体文件URL和字体文件URL到对应字体映射关系的二级缓存:

    from io import BytesIO
    
    url2FontMapCache = {}
    css2FontCache = {}
    
    
    def getFontMapFromURL(font_url):
        "缓存字体URL对应字体映射关系"
        if font_url not in url2FontMapCache:
            font_bytes = BytesIO(session.get(font_url).content)
            font_data = TTFont(font_bytes)
            uni_list = font_data.getGlyphOrder()[2:]
            url2FontMapCache[font_url] = dict(
                zip(map(lambda x: x[3:], uni_list), words))
        return url2FontMapCache[font_url]
    
    
    def getFontMapFromClassName(class_name, css_url):
        "缓存指定css文件对应字体URL"
        if css_url not in css2FontCache:
            css2FontCache[css_url] = parseCssFontUrl(css_url, ".woff")
        font_url = css2FontCache[css_url].get(class_name)
        return getFontMapFromURL(font_url)
    

    可以获取当前页面下,每个自定义字体的映射关系:

    for class_name in font_urls.keys():
        font_map = getFontMapFromClassName(class_name, css_url)
        print(list(font_map.items())[:12])
    

    结果:

    [('e0a7', '1'), ('ebf3', '2'), ('ee9b', '3'), ('e7e4', '4'), ('f5f8', '5'), ('e7a1', '6'), ('ef49', '7'), ('eef7', '8'), ('f7e0', '9'), ('e633', '0'), ('e5de', '店'), ('e67f', '中')]
    [('ec3e', '1'), ('f3fc', '2'), ('ea1f', '3'), ('e7f7', '4'), ('e258', '5'), ('f5aa', '6'), ('f48c', '7'), ('f088', '8'), ('f588', '9'), ('f82e', '0'), ('e7c5', '店'), ('e137', '中')]
    [('e3e0', '1'), ('e85f', '2'), ('f3c8', '3'), ('f3d5', '4'), ('e771', '5'), ('f251', '6'), ('f6f6', '7'), ('e8da', '8'), ('ea58', '9'), ('f8fb', '0'), ('ef9b', '店'), ('f3dd', '中')]
    [('ec3e', '1'), ('f3fc', '2'), ('ea1f', '3'), ('e7f7', '4'), ('e258', '5'), ('f5aa', '6'), ('f48c', '7'), ('f088', '8'), ('f588', '9'), ('f82e', '0'), ('e7c5', '店'), ('e137', '中')]
    

    将所有自定义字体全部替换为正常文字

    有了字体映射关系,我们就可以对页面的自定义字体替换成我们解析好的文本数据。

    首先获取被替换的父节点列表,方便对比:

    b_tags = [svgmtsi.parent for svgmtsi in soup.find_all('svgmtsi')]
    b_tags
    

    image-20210803101837065

    虽然我们现在看到该网站每个svgmtsi标签只存放一个字符,但无法确保以后也依然如此,所以我们的代码现在就考虑一个svgmtsi标签内部存在多个字符的情况。

    执行替换:

    for svgmtsi in soup.find_all('svgmtsi'):
        class_name = svgmtsi['class'][0]
        font_map = getFontMapFromClassName(class_name, css_url)
        chars = []
        for c in svgmtsi.text:
            char = c.encode("unicode_escape").decode()[2:]
            chars.append(font_map[char])
        svgmtsi.replaceWith("".join(chars))
    

    替换后,再查看之前保存的节点:

    b_tags
    

    image-20210803101902441

    提取数据

    将自定义字体替换之后,我们就可以非常丝滑的提取需要的数据了:

    num_rule = re.compile("\d+")
    
    for li_tag in soup.select("div#shop-all-list div.txt"):
        title = li_tag.select_one("div.tit>a>h4").text
        url = li_tag.select_one("div.tit>a")["href"]
        star_class = li_tag.select_one(
            "div.comment>div.nebula_star>div.star_icon>span")["class"]
        star = int(num_rule.findall(" ".join(star_class))[0])//10
    
        comment_tag = li_tag.select_one("div.comment>a.review-num>b")
        comment_num = comment_tag.text if comment_tag else None
    
        mean_price_tag = li_tag.select_one("div.comment>a.mean-price>b")
        mean_price = mean_price_tag.text if mean_price_tag else None
    
        fun_type = li_tag.select_one("div.tag-addr>a:nth-of-type(1)>span.tag").text
        area = li_tag.select_one("div.tag-addr>a:nth-of-type(2)>span.tag").text
        print(title, url, star, comment_num, mean_price, fun_type, area)
    
    轰趴天台·大白之家(南山店) http://www.dianping.com/shop/k1lqueFI6sIOfjnI 5 129 ¥196 轰趴馆 华侨城
    巨鹿搏击俱乐部(车公庙店) http://www.dianping.com/shop/k4tmabQaordrq6Tm 5 238 ¥246 拳击 车公庙
    SWING CAGE 棒球击球笼&冲浪滑板碗池 http://www.dianping.com/shop/l2lUP0rvcLPy4ebm 5 1088 ¥85 体育场馆 科技园
    微醺云深处沉浸式剧场 http://www.dianping.com/shop/HaLzYuXfvUWmPLSz 0 8 None 剧本杀 科技园
    逐见有光Chandelle http://www.dianping.com/shop/H2CSpvtn70y12wNh 5 138 ¥281 DIY手工坊 市中心/会展中心
    cozy cozy银饰DIY手作室(万象城店) http://www.dianping.com/shop/l3mCIs6dSrdL9jo7 5 1270 ¥321 DIY手工坊 万象城
    博哥的小剧场沉浸推理体验馆(南山万象天地店) http://www.dianping.com/shop/H3S4zD55e1gmfb8E 3 18 None 剧本杀 科技园
    FlowLife拓极滑板冲浪俱乐部(蛇口旗舰店) http://www.dianping.com/shop/G9sHgWISxYtBXz79 5 481 ¥217 新奇体验 蛇口
    Doors秘道·独立剧情密室(车公庙分店) http://www.dianping.com/shop/k4O3oDj6BwLtbgD4 5 878 ¥101 密室 车公庙
    御隆茶馆 http://www.dianping.com/shop/H6HEuBttJKlMkaAn 0 3 None 棋牌室 南头
    【十万伏特】手创空间 自由DIY http://www.dianping.com/shop/k5OURy1bNIs7ed7v 5 271 ¥152 DIY手工坊 梅林
    八町桑BATTING SOUND 棒球体验馆 http://www.dianping.com/shop/k9yQRAmYoa3o8cLI 5 734 ¥114 新奇体验 车公庙
    星美棋牌 http://www.dianping.com/shop/l4JRIjqLWi2zeFQd 3 9 None 棋牌室 国贸
    ZUO STUDIO烘焙课程· 茶歇蛋糕订购(南山京基百纳广场... http://www.dianping.com/shop/ER0EyDpjx36ekF0G 5 687 ¥224 DIY手工坊 白石洲
    cozy cozy银饰DIY手作室(南山店) http://www.dianping.com/shop/G7MbwkosLSvS3X1I 5 431 ¥338 DIY手工坊 南头
    

    批量下载

    经过以上测试,我们可以将所有相关方法都封装一下,下面我们下载深圳华南城的所有娱乐相关的团购信息:

    import re
    from bs4 import BeautifulSoup
    import requests
    import pandas as pd
    import random
    import time
    from urllib import parse
    from io import BytesIO
    from fontTools.ttLib import TTFont
    
    url2FontMapCache = {}
    css2FontCache = {}
    words = '1234567890店中美家馆小车大市公酒行国品发电金心业商司超生装园场食有新限天面工服海华水房饰城乐汽香部利子老艺花专东肉菜学福饭人百餐茶务通味所山区门药银农龙停尚安广鑫一容动南具源兴鲜记时机烤文康信果阳理锅宝达地儿衣特产西批坊州牛佳化五米修爱北养卖建材三会鸡室红站德王光名丽油院堂烧江社合星货型村自科快便日民营和活童明器烟育宾精屋经居庄石顺林尔县手厅销用好客火雅盛体旅之鞋辣作粉包楼校鱼平彩上吧保永万物教吃设医正造丰健点汤网庆技斯洗料配汇木缘加麻联卫川泰色世方寓风幼羊烫来高厂兰阿贝皮全女拉成云维贸道术运都口博河瑞宏京际路祥青镇厨培力惠连马鸿钢训影甲助窗布富牌头四多妆吉苑沙恒隆春干饼氏里二管诚制售嘉长轩杂副清计黄讯太鸭号街交与叉附近层旁对巷栋环省桥湖段乡厦府铺内侧元购前幢滨处向座下澩凤港开关景泉塘放昌线湾政步宁解白田町溪十八古双胜本单同九迎第台玉锦底后七斜期武岭松角纪朝峰六振珠局岗洲横边济井办汉代临弄团外塔杨铁浦字年岛陵原梅进荣友虹央桂沿事津凯莲丁秀柳集紫旗张谷的是不了很还个也这我就在以可到错没去过感次要比觉看得说常真们但最喜哈么别位能较境非为欢然他挺着价那意种想出员两推做排实分间甜度起满给热完格荐喝等其再几只现朋候样直而买于般豆量选奶打每评少算又因情找些份置适什蛋师气你姐棒试总定啊足级整带虾如态且尝主话强当更板知己无酸让入啦式笑赞片酱差像提队走嫩才刚午接重串回晚微周值费性桌拍跟块调糕'
    num_rule = re.compile("\d+")
    
    def get_url(urls, tag, only_First=True):
        urls = [parse.urljoin(base_url, url)
                for url in urls if tag is None or url.find(tag) != -1]
        if urls and only_First:
            return urls[0]
        return urls
    
    
    def parseCssFontUrl(css_url, tag=None, only_First=True):
        res = session.get(css_url)
        rule = {}
        font_face = {}
        for name, value in re.findall("([^{}]+){([^{}]+)}", res.text):
            name = name.strip()
            for row in value.split(";"):
                if row.find(":") == -1:
                    continue
                k, v = row.split(":")
                k, v = k.strip(), v.strip(' "\'')
                if name == "@font-face":
                    if k == "font-family":
                        font_name = v
                    elif k == "src":
                        font_face.setdefault(font_name, []).extend(
                            re.findall("url\(\"([^()]+)\"\)", v))
                else:
                    rule[name[1:]] = v
        font_urls = {}
        for class_name, tag_name in rule.items():
            font_urls[class_name] = get_url(font_face[tag_name], tag)
        return font_urls
    
    
    def getFontMapFromURL(font_url):
        "缓存字体URL对应字体映射关系"
        if font_url not in url2FontMapCache:
            font_bytes = BytesIO(session.get(font_url).content)
            font_data = TTFont(font_bytes)
            uni_list = font_data.getGlyphOrder()[2:]
            url2FontMapCache[font_url] = dict(
                zip(map(lambda x: x[3:], uni_list), words))
        return url2FontMapCache[font_url]
    
    
    def getFontMapFromClassName(class_name, css_url):
        "缓存指定css文件对应字体URL"
        if css_url not in css2FontCache:
            css2FontCache[css_url] = parseCssFontUrl(css_url, ".woff")
        font_url = css2FontCache[css_url].get(class_name)
        return getFontMapFromURL(font_url)
    
    
    def parse_data(soup):
        result = []
        for li_tag in soup.select("div#shop-all-list div.txt"):
            title = li_tag.select_one("div.tit>a>h4").text
            url = li_tag.select_one("div.tit>a")["href"]
            star_class = li_tag.select_one(
                "div.comment>div.nebula_star>div.star_icon>span")["class"]
            star = int(num_rule.findall(" ".join(star_class))[0])//10
    
            comment_tag = li_tag.select_one("div.comment>a.review-num>b")
            comment_num = comment_tag.text if comment_tag else None
    
            mean_price_tag = li_tag.select_one("div.comment>a.mean-price>b")
            mean_price = mean_price_tag.text if mean_price_tag else None
    
            fun_type = li_tag.select_one(
                "div.tag-addr>a:nth-of-type(1)>span.tag").text
            area = li_tag.select_one("div.tag-addr>a:nth-of-type(2)>span.tag").text
            result.append((title, star, comment_num,
                          mean_price, fun_type, area, url))
        return result
    
    
    def getUrlFromNode(nodes, tag):
        for node in nodes:
            url = node['href']
            if url.find(tag) != -1:
                return parse.urljoin(base_url, url)
    
    
    def get_css_url(soup):
        css_url = getUrlFromNode(soup.select(
            "head > link[rel=stylesheet]"), "svgtextcss")
        return css_url
    
    
    def fix_text(soup):
        css_url = get_css_url(soup)
        for svgmtsi in soup.find_all('svgmtsi'):
            class_name = svgmtsi['class'][0]
            font_map = getFontMapFromClassName(class_name, css_url)
            chars = []
            for c in svgmtsi.text:
                char = c.encode("unicode_escape").decode()[2:]
                chars.append(font_map[char])
            svgmtsi.replaceWith("".join(chars))
    
    
    headers = {
        "Connection": "keep-alive",
        "Cache-Control": "max-age=0",
        "Upgrade-Insecure-Requests": "1",
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36",
        "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
        "Accept-Language": "zh-CN,zh;q=0.9"
    }
    session = requests.Session()
    session.headers = headers
    base_url = "http://www.dianping.com/shenzhen/ch30"
    res = session.get(base_url)
    
    soup = BeautifulSoup(res.text, 'html5lib')
    type_list = []
    for a_tag in soup.select("div#classfy > a"):
        type_list.append((a_tag.span.text, a_tag['href']+'r91172'))
    
    result = []
    for type_name, url in type_list:
        print(type_name, url)
        res = session.get(url)
        soup = BeautifulSoup(res.text, 'html5lib')
        fix_text(soup)
        result.extend(parse_data(soup))
        time.sleep(random.randint(2, 4))
    
    df = pd.DataFrame(result, columns=["标题", "星级", "评论数", "均价", "娱乐类型", "区域", "链接"])
    df.评论数 = df.评论数.apply(lambda x: int(x) if x else pd.NA)
    df.均价 = df.均价.str[1:].apply(lambda x: int(x) if x else pd.NA)
    df.drop_duplicates(inplace=True)
    df.to_excel("华南城娱乐.xlsx", index=False)
    

    爬取结果(有一定的二次编辑):

    image-20210803104317180

    总结

    整体来说,该团购网站的反爬机制还是挺猛的,费了九牛二虎之力也就只能每个栏目爬一页数据,还没有地址,推荐各位不要去爬了。

    不过本文的目的就是演示把最难的字体反爬给解决掉,希望本文已经达到这个目标,如果后面还有更难的字体反爬网站出现,再继续更深的剖析,见招拆招。

    希望各位小伙伴,在研究完本文后,能够应对任何字体反爬问题。

    展开全文
  • 一:用jstable进行解析:    Table Test          function Delete()  {  var dgTable=document.getElementById('tb').rows;//tb为table的ID,  var pp=window.event.srcElement;     ...

    一:用js对table进行解析:

    <html>

     <head>
      <title>Table Test</title>
      <meta name="generator" content="Editplus" />
      <meta name="author" content="PJ" />
      <script language="JavaScript">
      <!--
       function Delete()
       {
        var dgTable=document.getElementById('tb').rows;//tb为table的ID,
        var pp=window.event.srcElement; 
        
        for (var i=0; i < dgTable.length; i++) //遍历table的行,
        {
         if(pp==dgTable[i].cells[2].getElementsByTagName("A")[0]) //cells[2]为触发事件的列的索引
         { //判断是否是触发事件的A标签
          alert(dgTable[i].cells[0].innerText); //演示,弹出所在行的第一列的内容    
         } 
        }
       }
      //-->
      </script>
     </head>
     <body>
      <table id="tb" border="1px" width="500px">
      <tr>
       <td width="40%">aaa</td>
       <td width="40%">bbb</td>
       <td width="15%"><a οnclick="Delete()" href="#">delete</a></td>
      </tr>
      <tr>
       <td>ccc</td>
       <td>ddd</td>
       <td><a οnclick="Delete()" href="#">delete</a></td>
      </tr>
      <tr>
       <td>eee</td>
       <td>fff</td>
       <td><a οnclick="Delete()" href="#">delete</a></td>
      </tr>
      </table>
     </body>

    </html>

    二:js修改table中文字颜色

    按理来说,这个实现应该很简单,直接找到文字,然后.style.color="red"即可,但是,试了几次,发现此法不奏效。取到文字之后直接改变的颜色的解决办法没有找到,希望有知道的可以告诉一下。这里我采用的是一个迂回之术,即先把文字拿出来,加上<font color>标签,再给放进去。


    function changeColor(id,name){
        var dgTable=document.getElementById('polling').rows;
        var tmpName = "";
        for (var i=1; i < dgTable.length; i++){ //遍历table的行,
        tmpName= dgTable[i].cells[2].innerText;
        tmpName = tmpGroupName.trim();
        if(tmpName == name){  //选中该行
                dgTable[i].cells[2].innerHTML = "<font color=\"#0000FF\">" + tmpName + "</font>";
                document.getElementById("groupLanesFrm").src = path + "/systemConfigAction!showGroupLanes.action?groupId=" + id;
        }else{
        dgTable[i].cells[2].innerHTML = "<u>" + tmpName + "</u>";
        }
        }
        //document.getElementById("groupLanesFrm").src = path + "/systemConfigAction!showGroupLanes.action?groupId=" + id;
        }


    PS:


    innerText和innerHtml的区别:


    innerText可以用来修改开始标签和结束标签之间的内容。
    odiv.innerText = "hello";
    innerText可以自动将一些关键字符编码(<,>,&),无须担心特殊字符。
    innerHtml将标签在浏览器中解读。


    对比:
    var odiv = doucment.getElementById("div1");
    odiv1.innerText = "<h1>HELLO</h1>"


    会显示为<h1>HELLO</h1>
    当将innerText改为innerHtml时,将会显示<H1>格式的HELLO











    展开全文
  • Pythonhtml解析(BeautifulSoup)

    万次阅读 2020-11-05 16:57:54
    BeautifulSoup支持不同的解析器,比如,HTML解析XML解析HTML5解析。一般情况下,我们用的比较多的是 lxml 解析器。 BeautifulSoup安装  BeautifulSoup3 目前已经停止更新,推荐在现在的项目中使用...

    微信搜索:“二十同学” 公众号,欢迎关注一条不一样的成长之路

    BeautifulSoup简介

      BeautifulSoup是一个高效的网页解析库,可以从 HTML 或 XML 文件中提取数据。BeautifulSoup支持不同的解析器,比如,对HTML解析,对XML解析,对HTML5解析。一般情况下,我们用的比较多的是 lxml 解析器。

    BeautifulSoup安装

      BeautifulSoup3 目前已经停止更新,推荐在现在的项目中使用BeautifulSoup4,不过它已经被移植到bs4了。也就是说导入时我们需要 import bs4 。可以利用 pip 或者 easy_install 两种方法来安装。下面采用pip安装。

      pip install beautifulsoup4

      pip install lxml

      建议同时安装"lxml"模块,BeautifulSoup支持Python标准库中的HTML解析器("html.parser"),还支持一些第三方的解析器,如果我们不安装它,则 Python 会使用 Python默认的解析器,lxml 解析器更加强大,速度更快,推荐安装。

    创建对象

      安装后,创建对象:soup = BeautifulSoup(markup='类html字符串', 'lxml')  # 这里有个坑,"lxml" 解析器会将所有节点名称转为小写!使用 "xml" 解析器则不会转换。所以操作XML用“xml”,操作HTML用“lxml”。

      格式化输出:soup.prettify()

    BeautifulSoup四大对象类型

      BeautifulSoup将复杂HTML文档转换成一个复杂的树形结构,每个节点都是Python对象,所有对象可以归纳为4种:

    • BeautifulSoup(文档)
    • Tag(标签)
    • NavigableString(内容)
    • Comment(注释)

    1.BeautifulSoup类型(文档)

      BeautifulSoup对象表示的是一个文档的全部内容:

    print soup.name 
    # [document]

    2.Tag类型(标签)

      即HTML的整个标签,如获取<title>标签:

    print soup.title
    #<title>The Dormouse's story</title>

      Tag有两个重要属性:name,attrs。

    name

      即HTML的标签名称:

    print soup.name
    #[document]
    print soup.head.name
    #head

    attrs

      即HTML的标签属性字典:

    print soup.p.attrs
    #{'class': ['title'], 'name': 'dromouse'}

      如果想要单独获取某个属性:

    print soup.p['class']
    #['title']

    3.NavigableString类型(内容)

      既然我们已经得到了整个标签,那么问题来了,我们要想获取标签内部的文字内容怎么办呢?很简单,用 string 即可:

    print soup.p.string
    #The Dormouse's story

    4.Comment类型(注释)

      HTML的注释内容,注意的是,不包含注释符号。我们首先判断它的类型,是否为 Comment 类型,然后再进行其他操作,如打印输出:

    if type(soup.a.string)==bs4.element.Comment:
        print soup.a.string
    #<!-- Elsie -->

    遍历文档树

    1.子节点

    contents

      获取所有子节点,返回列表:

    print soup.head.contents 
    #[<title>The Dormouse's story</title>]

    children

      获取所有子节点,返回列表生成器:

    print soup.head.children
    #<listiterator object at 0x7f71457f5710>
    
    ## 需要遍历
    for child in  soup.body.children:
        print child
    
    ## 结果
    <p class="title" name="dromouse"><b>The Dormouse's story</b></p>
    
    <p class="story">Once upon a time there were three little sisters; and their names were
    <a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>,
    <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a> and
    <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>;
    and they lived at the bottom of a well.</p>
    
    
    <p class="story">...</p>

    2.节点内容

    string

      返回单个文本内容。如果一个标签里面没有标签了,那么 string 就会返回标签里面的内容。如果标签里面只有唯一的一个标签了,那么 string 也会返回最里面的内容。如果tag包含了多个子节点,tag就无法确定,string 方法应该调用哪个子节点的内容,string 的输出结果是 None。例如:

    print soup.head.string
    print soup.title.string
    #The Dormouse's story
    #The Dormouse's storyprint soup.html.string# None

    strings

      返回多个文本内容,且包含空行和空格。

    stripped_strings

      返回多个文本内容,且不包含空行和空格:

    for string in soup.stripped_strings:
        print(repr(string))
        # u"The Dormouse's story"
        # u"The Dormouse's story"
        # u'Once upon a time there were three little sisters; and their names were'
        # u'Elsie'
        # u','
        # u'Lacie'
        # u'and'
        # u'Tillie'
        # u';\nand they lived at the bottom of a well.'
        # u'...'

    get_text()方法

      返回当前节点和子节点的文本内容。

    from bs4 import BeautifulSoup
    
    html_doc = """
    <html><head><title>The Dormouse's story</title></head>
    <body>
        <p class="title"><b>The Dormouse's story</b></p>
        <p class="story">Once upon a time there were three little sisters; and their names were
            <a href="http://example.com/elsie" class="sister1" id="link1">Elsie</a>,
            <a href="http://example.com/lacie" class="sister2" id="link2">Lacie</a> and
            <a href="http://example.com/tillie" class="sister3" id="link3">Tillie</a>;
            and they lived at the bottom of a well.
        </p>
        <p class="story">...</p>
    </body>
    </html>
    """
    
    soup = BeautifulSoup(markup=html_doc,features='lxml')
    
    node_p_text=soup.find('p',class_='story').get_text()    # 注意class_带下划线
    print(node_p_text)
    
    # 结果
    Once upon a time there were three little sisters; and their names were
            Elsie,
            Lacie and
            Tillie;
            and they lived at the bottom of a well.

    3.父节点

    parent

      返回某节点的直接父节点:

    p = soup.p
    print p.parent.name
    #body

    parents

      返回某节点的所有父辈及以上辈的节点:

    content = soup.head.title.string
    for parent in  content.parents:
        print parent.name
    
    ## 结果
    title
    head
    html
    [document]

    4.兄弟节点

    next_sibling

      next_sibling 属性获取该节点的下一个兄弟节点,结果通常是字符串或空白,因为空白或者换行也可以被视作一个节点。

    previous_sibling

      previous_sibling 属性获取该节点的上一个兄弟节点。

    print soup.p.next_sibling
    #       实际该处为空白
    print soup.p.prev_sibling
    #None   没有前一个兄弟节点,返回 None
    print soup.p.next_sibling.next_sibling
    #<p class="story">Once upon a time there were three little sisters; and their names were
    #<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>,
    #<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a> and
    #<a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>;
    #and they lived at the bottom of a well.</p>
    #下一个节点的下一个兄弟节点是我们可以看到的节点

    next_siblings、previous_siblings

      迭代获取全部兄弟节点。

    5.前后节点

    next_element、previous_element

      不是针对于兄弟节点,而是在于所有节点,不分层次的前一个和后一个节点。

    next_elements、previous_elements

      迭代获取所有前和后节点。

    搜索文档树

    1.find_all(name=None, attrs={}, recursive=True, text=None, limit=None, **kwargs)

      find_all()方法搜索当前tag的所有tag子节点,并判断是否符合过滤器的条件。

    参数说明

    name参数

      name参数很强大,可以传多种方式的参数,查找所有名字为 name 的tag,字符串对象会被自动忽略掉。

    (a)传标签名  最简单的过滤器是标签名。在搜索方法中传入一个标签名参数,BeautifulSoup会查找与标签名完整匹配的内容,下面的例子用于查找文档中所有的<a>标签:

    print soup.find_all('a')
    #[<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>, <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>, <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]

      返回结果列表中的元素仍然是BeautifulSoup对象。

    (b)传正则表达式

      如果传入正则表达式作为参数,BeautifulSoup会通过正则表达式的 match() 来匹配内容。下面例子中找出所有以b开头的标签,这表示<body>和<b>标签都应该被找到:

    import re
    for tag in soup.find_all(re.compile("^b")):
        print(tag.name)
    # body
    # b

    (c)传列表

      如果传入列表参数,BeautifulSoup会将与列表中任一元素匹配的内容返回。下面代码找到文档中所有<a>标签和<b>标签:

    soup.find_all(["a", "b"])
    # [<b>The Dormouse's story</b>,
    #  <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
    #  <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
    #  <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]

    (d)传True

      True 可以匹配任何值,下面代码查找到所有的tag,但是不会返回字符串节点:

    for tag in soup.find_all(True):
        print(tag.name)
    # html
    # head
    # title
    # body
    # p
    # b
    # p
    # a
    # a

    (e)传函数

      如果没有合适过滤器,那么还可以定义一个方法,方法只接受一个元素参数。如果这个方法返回 True 表示当前元素匹配并且被找到,如果不是则反回 False:

    def has_class_but_no_id(tag):
        return tag.has_attr('class') and not tag.has_attr('id')
    
    soup.find_all(has_class_but_no_id)
    # [<p class="title"><b>The Dormouse's story</b></p>,
    #  <p class="story">Once upon a time there were...</p>,
    #  <p class="story">...</p>]

    keyword参数  注意的是,如果一个指定名字的参数不是搜索内置的参数名,搜索时会把该参数当作指定名字tag的属性来搜索,如果包含一个名字为 id 的参数,BeautifulSoup会搜索每个tag的”id”属性:

    soup.find_all(id='link2')
    # [<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]

      如果传入 href 参数,Beautiful Soup会搜索每个tag的"href"属性:

    soup.find_all(href=re.compile("elsie"))
    # [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>]

      使用多个指定名字的参数可以同时过滤tag的多个属性:

    soup.find_all(href=re.compile("elsie"), id='link1')
    # [<a class="sister" href="http://example.com/elsie" id="link1">three</a>]

      在这里我们想用 class 过滤,不过 class 是 python 的关键词,这怎么办?加个下划线就可以:

    soup.find_all("a", class_="sister")
    # [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
    #  <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
    #  <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]

    attrs参数

      有些tag属性在搜索不能使用,比如HTML5中的 " data-* " 自定义属性:

    data_soup = BeautifulSoup('<div data-foo="value">foo!</div>')
    data_soup.find_all(data-foo="value")
    # SyntaxError: keyword can't be an expression
    
    ## 但是可以通过 find_all() 方法的 attrs 参数定义一个字典参数来搜索包含特殊属性的tag
    data_soup.find_all(attrs={"data-foo": "value"})
    # [<div data-foo="value">foo!</div>]

    text参数

      通过 text 参数可以搜搜文档中的字符串内容。与 name 参数的可选值一样,text 参数接受字符串 、正则表达式 、列表、True。

    soup.find_all(text="Elsie")
    # [u'Elsie']
    
    soup.find_all(text=["Tillie", "Elsie", "Lacie"])
    # [u'Elsie', u'Lacie', u'Tillie']
    
    soup.find_all(text=re.compile("Dormouse"))  # 模糊查找
    [u"The Dormouse's story", u"The Dormouse's story"]

    limit参数

      find_all() 方法返回全部的搜索结构,如果文档树很大那么搜索会很慢。如果我们不需要全部结果,可以使用 limit 参数限制返回结果的数量。效果与SQL中的limit关键字类似,当搜索到的结果数量达到 limit 的限制时,就停止搜索返回结果。

    soup.find_all("a", limit=2)
    # [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
    #  <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]

    recursive参数

      调用tag的 find_all() 方法时,BeautifulSoup会检索当前tag的所有子孙节点,如果只想搜索tag的直接子节点,可以使用参数 recursive=False。

    soup.html.find_all("title")
    # [<title>The Dormouse's story</title>]
    
    soup.html.find_all("title", recursive=False)
    # []

    2.find( name , attrs , recursive , text , **kwargs )

      它与 find_all() 方法唯一的区别是 find_all() 方法的返回结果是值包含一个元素的列表,而 find() 方法直接返回结果。

    3.find_parents() 和 find_parent()

      find_all() 和 find() 只搜索当前节点的所有子节点,孙子节点等。find_parents() 和 find_parent() 用来搜索当前节点的父辈节点,搜索方法与普通tag的搜索方法相同,搜索文档搜索文档包含的内容。

    4.find_next_siblings() 和 find_next_sibling()  

      这2个方法通过 .next_siblings 属性对当 tag 的所有后面解析的兄弟 tag 节点进行迭代, find_next_siblings() 方法返回所有符合条件的后面的兄弟节点,find_next_sibling() 只返回符合条件的后面的第一个tag节点。

    5.find_previous_siblings() 和 find_previous_sibling()

      这2个方法通过 .previous_siblings 属性对当前 tag 的前面解析的兄弟 tag 节点进行迭代, find_previous_siblings() 方法返回所有符合条件的前面的兄弟节点,find_previous_sibling() 方法返回第一个符合条件的前面的兄弟节点。

    6.find_all_next() 和 find_next()

      这2个方法通过 .next_elements 属性对当前 tag 的之后的 tag 和字符串进行迭代, find_all_next() 方法返回所有符合条件的节点, find_next() 方法返回第一个符合条件的节点。

    7.find_all_previous() 和 find_previous()

      这2个方法通过 .previous_elements 属性对当前节点前面的 tag 和字符串进行迭代,find_all_previous() 方法返回所有符合条件的节点, find_previous()方法返回第一个符合条件的节点。

     CSS选择器

       我们在写 CSS 时,标签名不加任何修饰,类名前加点,id名前加 #,在这里我们也可以利用类似的方法来筛选元素,用到的方法是 soup.select(),返回类型是 list。

    通过标签名查找

    print soup.select('title') 
    #[<title>The Dormouse's story</title>]
    
    print soup.select('a')
    #[<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>, <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>, <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
    
    print soup.select('b')
    #[<b>The Dormouse's story</b>]

    通过类名查找

    print soup.select('.sister')
    #[<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>, <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>, <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]

    通过 id 名查找

    print soup.select('#link1')
    #[<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>]

    组合查找

      组合查找即和写 class 文件时,标签名与类名、id名进行的组合原理是一样的,例如查找 p 标签中,id 等于 link1的内容,二者需要用空格分开。

    print soup.select('p #link1')
    #[<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>]

      直接子标签查找:

    print soup.select("head > title")
    #[<title>The Dormouse's story</title>]

    属性查找

      查找时还可以加入属性元素,属性需要用中括号括起来,注意属性和标签属于同一节点,所以中间不能加空格,否则会无法匹配到。

    print soup.select('a[class="sister"]')
    #[<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>, <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>, <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
    
    print soup.select('a[href="http://example.com/elsie"]')
    #[<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>]

      同样,属性仍然可以与上述查找方式组合,不在同一节点的空格隔开,同一节点的不加空格:

    print soup.select('p a[href="http://example.com/elsie"]')
    #[<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>]

      以上的 select 方法返回的结果都是列表形式,可以遍历形式输出,然后用 string或get_text() 方法来获取它的内容:

    soup = BeautifulSoup(html, 'lxml')
    print type(soup.select('title'))
    print soup.select('title')[0].get_text()
    
    for title in soup.select('title'):
        print title.get_text()

    修改文档树

    1.修改.string

    给Tag的 .string 属性赋值,就相当于用当前的内容替代了原来的内容。(如果当前的Tag包含了其它tag,那么给它的 .string 属性赋值会覆盖掉原有的所有内容包括子Tag)。

    如,soup.find('title').string = '新标题'

    2.append()

    Tag.append() 方法向Tag中添加子内容,与Python的列表的 append() 方法用法相同。

    如,soup.find('p').append('段落内容')

    3.new_tag()

    创建一个Tag最好的方法是调用工厂方法 BeautifulSoup.new_tag()。

    如,soup.find('p').append(soup.new_tag('a', href='http://www.baidu.com'))

    4.insert()、insert_before() 和 insert_after()

    把元素插入到指定的位置。

    如,soup.find('p').insert_after(soup.new_tag('a', href='http://www.baidu.com'))

    5.decompose()

    Tag.decompose() 方法将当前节点完全移除。

    6.clear()

    Tag.clear() 方法清空当前节点的内容

    展开全文
  • yolo系列之yolo v3【深度解析

    万次阅读 多人点赞 2018-09-12 16:24:48
    很多骚年入手yolo算法都是v3才开始,这是不可能掌握yolo精髓的,因为v3很多东西是保留v2甚至v1的东西,而且v3的论文写得很随心。想深入了解yolo_v3算法,必须先了解v1和v2。以下是我关于v1和v2算法解析所写的文章...
  • 前段时间在LeetCode上刷题,遇到了很多涉及字符串进行解析的题目。可能是出于这个原因,最近迷恋上了字符串的解析问题。数学基本运算表达式的解析就涉及这类问题。所谓数学基本运算表达式的解析就是指给定一个...
  • 【7万干货】2021Java实习必看面试两百题解析

    万次阅读 多人点赞 2020-03-30 12:35:46
    答:有问题,因为3.4 是双精度数,将双精度型(double)赋值给浮点型(float)属于向下转型,可能会造成精度损失,所以必须进行强制类型转换,正确的写法是float number =(float)3.4;/ float number =3.4F;。 Q2:...
  • 怎样通过dnspod进行域名解析

    千次阅读 2015-05-30 14:53:27
    如果域名是国外注册的,一般需要使用第三方DNS服务器来解析域名。 首先要DNSPod注册用户。打开DNSPod网站,按照要求填写资料。新注册了一个域名后,并无法直接使用,而要访问网络上的服务器,必须通过服务器的IP...
  • Google Geocoding API ... 地址解析请求 Google Geocoding API 请求必须采用以下形式: http://maps.google.com/maps/api/geocode/output?parameters
  • 经过前几个章节的介绍,终于把与列表解析的前置内容介绍完了,本节老猿将列表解析、字典解析、集合解析进行统一的介绍。 前面章节老猿好几次说到了要介绍列表解析,但老猿认为涉及知识层面比较多,一推再推,给人的...
  • 如果是,则字符串进行解析,直到到达数字的末端为止,然后以数字返回该数字,而不是作为字符串。 使用规则: 1、parseFloat 是全局函数,不属于任何对象。 2、parseFloat 将它的字符串参数解析成为浮点数并返回...
  • 1.1 PDFBOX介绍 Apache PDFBox是一个开源Java库,支持PDF文档的开发...Extract Text - 使用PDFBox,您可以PDF文件中提取Unicode文本。 Split & Merge - 使用PDFBox,您可以将单个PDF文件分成多个文件,并将它...
  • poi解析excel读取日期为数字的问题

    万次阅读 2017-04-20 09:17:55
    今天在用poi解析excel文件时,碰到一个蛋疼的问题。 在我的excel文件中有一列是日期类型,例如有以下这么一行数据(日期中月份前面的0会自动去掉): 在读取注册日期这个数据时,返回了一串数字,变成了 42149, ...
  • 首发来自微信公众号:数字芯片设计11.多bit总线信号可以通过格雷码转换进行异步处理,例如:8bit的数据总线进行格雷码转换,然后通过双触发器法实现异步处理()【A】正确【B】错误解析;...
  • 报文解析

    千次阅读 2018-10-08 10:22:28
    用HttpClinet 来获取响应报文,document文档解析响应报文  为了结构明确,层次分明,我将冗长的方法封装在了多个方法中,在主方法中体现功能调用顺序,这样思路更清晰些。大家可以看主方法的中的调用顺序进行学习...
  • Date日期进行格式化和文本解析

    千次阅读 2019-08-22 20:28:50
    DateFormat是一个抽象类继承了Format类 日期/时间进行格式化 格式化(也就是日期–>文本)、解析 (文本 -->日期) 成员方法: String format(Date date) 按照指定的模式,把Date日期,格式化符合模式的字符串...
  • 但是写好之后发现前端直接将html代码以文字形式展示,未进行解析。 解决方式 添加v-html属性,文字中的html代码部分,将被解析并显示对应代码。 参考文章 Vue 将后台传过来的 带html字段的字符串 转换为 HT...
  • OracleSql语句的软解析和硬解析

    千次阅读 2013-07-06 05:57:13
    经常在论坛中,有人会在针对SQL优化方向提出:要避免SQL进行解析,从而提高SQL执行的效率。避免硬解析,确实是高效利用shared_pool的一种重要策略。通常情况下,作为开发人员,我们需要记住,为了最高效的利用共享池...
  • 文章目录一、DNS系统1、DNS的作用2、DNS概述3、DNS的定义4、域名结构:二、DNS域名解析方式:三、DNS服务器类型四、构建DNS域名正反向解析服务器步骤1、安装bind软件包2、配置正向解析3、反向解析 一、DNS系统 1、...
  • Oracle 硬解析与软解析

    万次阅读 2011-02-19 21:37:00
    Oracle 硬解析与软解析是我们经常遇到的问题,什么情况会产生硬解析,什么情况产生软解析,又当如何避免硬解析?CURSOR_SHARING 参数?
  • 命令解析--实现硬件的控制

    千次阅读 2018-12-09 11:31:03
    底层实现数据的采集、分类、打包、发送,同时还要可以接收并解析上位机传递下来的命令。下位机通过串口来接收上位机下发的命令,然后进行解析命令,然后去控制对应的硬件。
  • winpcap抓包并进行协议解析

    热门讨论 2010-07-15 23:12:06
    一般网上的抓包程序都是利用原始套接,而基于原始套接的抓包程序是无法抓到网络层一下的包的,如ARP包。本程序实现的任意类型的抓包。 资源中含有1.程序源码 2.winpcap安装程序3.课程设计文档4.VC++设置说明。...
  • 最近项目开发过程涉及到了pdf文件的内容的解析和和内容的提取入库操作,其中pdf的解析采用了开源的apache pdfbox 插件,版本选用的是最新版本的2.0.8版本,现将简单的读取解析的步骤记录如下: 1、导入jar,基础的...
  • 前言目前在很多小型网站上,广泛的存在着文件上传漏洞,在进行渗透时,通过sql注入/弱口令爆破进入后台+webshell文件上传也许就是你拿下它最快的方式。但是仅靠文件上传往往不行,有了解析漏洞的帮助效果可能会...
  • 二维码解析成链接 二维码解码

    千次阅读 2019-10-17 15:59:36
    左侧上传二维码就可以进行解析,右边会自动生成网址链接, 点击去解析
  • 有了bean类之后,我们可以数据进行封装,因为前面我们已经解析了数据嘛! 数据封装: 数据封装核心类,其实和这个解析类在一起了,那么个类的功能就是解析数据,并封装数据,返回一个bean类 package ...
  • 采用Stanford Parser进行中文语法解析

    万次阅读 2013-11-21 19:26:14
    Stanford Parser是由StanforsNLP Group开发的基于Java的开源NLP工具,支持中文的语法分析,当前最新的版本为3.3.0,...解压文件中lexparser-gui.bat进行可视化页面运行,解析需要的模型文件存放在stanford-parser-3.
  • 使用BeautifulSoup4解析XML

    万次阅读 多人点赞 2018-11-22 10:04:59
    Beautiful Soup 是一个用来HTML或XML文件中提取数据的Python库,它利用大家所喜欢的解析器提供了许多惯用方法用来文档树进行导航、查找和修改。 帮助文档英文版:...
  • 一般来讲,我们会网络获取XML或者Json格式的数据,这些数据有着特定的数据结构,必须进行解析,得到我们可以处理的数据。所谓“解析”,就是从事先规定好的格式串中提取数据。解析的前提是数据的提供方与获取...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 600,200
精华内容 240,080
关键字:

对从字进行解析