Python爬虫:细说列表识别提取

天冷要保暖

  上次文章后不少小伙伴私信我,对此感兴趣,希望我讲讲列表识别的细节问题。于是有了今天这篇文章。还是先再提一下本算法的核心思想。

  • 排列规则的链接为可以列表块。
  • 列表块范围在主视图区域内为目标列表。
    先看下识别结果:
    列表识别结果图

    提取列表区域xpath宏观分成三个大步骤

1.可疑列表区域提取

  在进行可疑列表区域提取之前需要做一些预处理:因为selenium只能定位到页面上的可见元素,所以先用selenium的find_elements_by_xpath("//a")获取所有可见的<a>,并对定位到的元素创建新属性canSee并赋值yeap:self.__web_driver.execute_script("arguments[0].setAttribute('can-see','yeap');", link)(其实你想属性叫什么就叫什么),然后就清洗完毕了。
  接下来用lxml库的etree定位到dom树(这里将html直接说成dom树是为了后面提取最小父节点时候好理解)上所有canSee属性是True的<a>标签节点。将该节点假如列表A,以三个元素为单位扫描该列表,如下图。

可疑列表区域扫描过程示意图

Tips:
  • 在整个dom树下,同一子树同一层级的节点才会提取最小父节点(最小父节点:层级尽可能的小
  • 重复父节点的xpath不要重复计入
  • 有的<a>标签下取text会出现问题,最好用xpath的string(.)方式
  • 元素清洗时可以初步匹配明显反向特征,匹配成功直接退出

  在三大步骤中,只有这一步是在做加法,剩下的步骤基本是在过滤做减法了,所以尽可能的将可疑列表区域收入列表。

代码流程参考:
def tag_a_min_father_node(self):
    """
    计算提取可疑列表区域        
    :return: [xpath1,xpath2,xpath3,...]
    """
    links_Ele = []
    father_list = []
    # 预处理-将可见<a>设置属性can-see
    self.watch_links()

    root = etree.HTML(self.driver.page_source)
    Eleroot = etree.ElementTree(root)
    temp_total_path = []
    links =  Eleroot.findall('//a[@cansee]')
    self.LOG.info("all links after filter: {}".format(len(links)))
    # 识别时忽略JavaScript,因为后续步骤没有上下文环境
    links_Ele = [(x.xpath("string(.)")..strip(),\
                  Eleroot.getpath(x), \
                  x.attrib.get("href","")) \
                  for x in links \
                  if x.xpath("string(.)"). and len(x.xpath("string(.)").strip()) > 1 and \
                       self.anchor_black_regx.search(x.xpath("string(.)").strip()) is None\
                      and self.debar_extension_name_regex.search(x.attrib.get("href","")) is None \
                      and not x.attrib.get("href","").startswith("java")\
                      and not x.attrib.get("href","").startswith("#") # 不要锚链接
                     ]
    # 元素清洗
    # 相邻标签相同href,合并
    # 如果匹配到反向特征 legitimate = False
    legitimate, links_Ele = self.clean_links_Ele(links_Ele)

    if legitimate:
        # 扫描有效链接,提取最小父节点xpath
        for idx in xrange(len(links_Ele)-2):
            # 每次取三个元素
            now = links_Ele[idx: idx+3]
            is_list, father_xpath = self.get_list_father_xpath(now)
            if is_list:
                # 符合列表逻辑
                father_list.append(father_xpath)

    return list(set(father_list))

2.过滤不在主视图区域的可疑列表

  • 2.1 校验x轴
      在这该步骤中,校验可疑列表区域是否在主视图范围内。你需要了解selenium的location方法,了解(x,y)坐标点在浏览器中的意义,在该算法中,使用x轴中位线作为判断依据。
      现有列表区域A,其location为(x1,y1)。列表A中,有最大链接b,其size['width']为x2。若x1+x2 > x轴中位线,则列表A在主视图范围内。
      看下图,不难理解:
    红线为x轴中位线

代码流程参考:

def judge_list_xpath(self):
    """
    判断获取到的列表xpath是否在主视图区域
    :return:[xpath1,xpath2]
    """
    a_list= []
    list_xpath = []
    result = []
    # 获取可疑列表区域
    list_xpath = self.get_page_list()

    if list_xpath:
        for item in list_xpath:
            a_size_list = []
            try:
                a_list = self.driver.find_elements_by_xpath(item + '//a')
            except:
                self.LOG.error("{}:{}无法找到该xpath" .format(self.driver.current_url, item + '//a'))

            for element in a_list:
                a_size_list.append(element.size['width'])
            # 有的html可能不规范,会出现定位不到元素的情况
            max_a_size = max(a_size_list) if len(a_size_list) > 0 else 0
            if max_a_size == 0: continue
            # 判断size最大的a标签的位置
            content = self.driver.find_element_by_xpath(item)
            # 超过3000认为异常情况
            if content.size['width'] > 3000:
                continue
            # 这句无所谓,原来想用来过滤导航栏之类的,现在后续有更好解决方案
            if (content.size['height']) < 70 and content.size['height'] != 0:
                continue
            # 判断x轴中位线
            if self.check_x(content):
                    result.append(item)
    self.LOG.info("list after view filter: {}".format(result))
    return result
  • 2.2校验y轴
      这一步需要放在程序最后,规则也比较简单,最后校验列表的location['y']是否在浏览器的当前页面中,我认为如果你打开网页,一下看不见列表,需要往下拖才有列表,就不是我们需要的主列表了,可能是混进来奇奇怪怪的东西,逻辑比较简单就不贴代码流程了。

3.可扩展规则簇

  以上步骤基本可以保证你获得一个穿过了x中位线的列表区域,但极有可能混进去一些奇奇怪怪的东西,或者漏了一些重要的东西。这时候就需要你的这些规则了,比如:

  • 多块列表跨x中位线
  • 识别到导航栏或者识别到滚动栏中的新闻,不需要这种东西,需要过滤
    • 过滤规则很简单,校验xpath中<a>的y坐标,极大值与极小值需要超过一个阀值
  • 中央区域含有文本为更多的链接,我相信这种列表也不是我们需要的
    还有后续其他的规则往上追加就好OvO

至此列表区域识别已经完成,输出值为列表区域的xpath。
有问题的话私聊我吧,没问题的话点赞吧~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270