??上次文章后不少小伙伴私信我,對此感興趣缓淹,希望我講講列表識別的細節(jié)問題哈打。于是有了今天這篇文章。還是先再提一下本算法的核心思想讯壶。
- 排列規(guī)則的鏈接為可以列表塊料仗。
-
列表塊范圍在主視圖區(qū)域內為目標列表。
先看下識別結果:
提取列表區(qū)域xpath宏觀分成三個大步驟
1.可疑列表區(qū)域提取
??在進行可疑列表區(qū)域提取之前需要做一些預處理:因為selenium只能定位到頁面上的可見元素伏蚊,所以先用selenium的find_elements_by_xpath("http://a")
獲取所有可見的<a>立轧,并對定位到的元素創(chuàng)建新屬性canSee并賦值yeap:self.__web_driver.execute_script("arguments[0].setAttribute('can-see','yeap');", link)
(其實你想屬性叫什么就叫什么),然后就清洗完畢了丙挽。
??接下來用lxml庫的etree定位到dom樹(這里將html直接說成dom樹是為了后面提取最小父節(jié)點時候好理解)上所有canSee屬性是True的<a>標簽節(jié)點肺孵。將該節(jié)點假如列表A,以三個元素為單位掃描該列表颜阐,如下圖平窘。
Tips:
- 在整個dom樹下,同一子樹同一層級的節(jié)點才會提取最小父節(jié)點(最小父節(jié)點:層級盡可能的小)
- 重復父節(jié)點的xpath不要重復計入
- 有的<a>標簽下取text會出現(xiàn)問題凳怨,最好用xpath的string(.)方式
- 元素清洗時可以初步匹配明顯反向特征瑰艘,匹配成功直接退出
??在三大步驟中,只有這一步是在做加法肤舞,剩下的步驟基本是在過濾做減法了紫新,所以盡可能的將可疑列表區(qū)域收入列表。
代碼流程參考:
def tag_a_min_father_node(self):
"""
計算提取可疑列表區(qū)域
: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李剖,因為后續(xù)步驟沒有上下文環(huán)境
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:
# 掃描有效鏈接,提取最小父節(jié)點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.過濾不在主視圖區(qū)域的可疑列表
-
2.1 校驗x軸
??在這該步驟中篙顺,校驗可疑列表區(qū)域是否在主視圖范圍內偶芍。你需要了解selenium的location方法充择,了解(x,y)坐標點在瀏覽器中的意義匪蟀,在該算法中椎麦,使用x軸中位線作為判斷依據(jù)。
??現(xiàn)有列表區(qū)域A,其location為(x1,y1)材彪。列表A中观挎,有最大鏈接b,其size['width']為x2段化。若x1+x2 > x軸中位線嘁捷,則列表A在主視圖范圍內。
??看下圖穗泵,不難理解:
代碼流程參考:
def judge_list_xpath(self):
"""
判斷獲取到的列表xpath是否在主視圖區(qū)域
:return:[xpath1,xpath2]
"""
a_list= []
list_xpath = []
result = []
# 獲取可疑列表區(qū)域
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可能不規(guī)范普气,會出現(xiàn)定位不到元素的情況
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
# 這句無所謂,原來想用來過濾導航欄之類的佃延,現(xiàn)在后續(xù)有更好解決方案
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軸
??這一步需要放在程序最后,規(guī)則也比較簡單夷磕,最后校驗列表的location['y']
是否在瀏覽器的當前頁面中履肃,我認為如果你打開網(wǎng)頁,一下看不見列表坐桩,需要往下拖才有列表尺棋,就不是我們需要的主列表了,可能是混進來奇奇怪怪的東西绵跷,邏輯比較簡單就不貼代碼流程了膘螟。
3.可擴展規(guī)則簇
??以上步驟基本可以保證你獲得一個穿過了x中位線的列表區(qū)域,但極有可能混進去一些奇奇怪怪的東西碾局,或者漏了一些重要的東西荆残。這時候就需要你的這些規(guī)則了,比如:
- 多塊列表跨x中位線
- 是否只是一整個列表的分塊净当,如(http://www.chinasafety.gov.cn/newpage/aqbz/aqbz_gjbzgb.htm)需要融合内斯。
- 又或者真的是多個列表,需要過濾像啼,如(http://www.cbrc.gov.cn/shanxi/pcjgMore/601108/left.html)
- 識別到導航欄或者識別到滾動欄中的新聞俘闯,不需要這種東西,需要過濾
- 過濾規(guī)則很簡單忽冻,校驗xpath中<a>的y坐標真朗,極大值與極小值需要超過一個閥值
- 中央?yún)^(qū)域含有文本為更多的鏈接,我相信這種列表也不是我們需要的
還有后續(xù)其他的規(guī)則往上追加就好OvO
至此列表區(qū)域識別已經(jīng)完成僧诚,輸出值為列表區(qū)域的xpath遮婶。
有問題的話私聊我吧蝗碎,沒問題的話點贊吧~