获取具有随机类名的元素

时间:2018-05-31 20:23:35

标签: python selenium web-scraping

Instagram的网页上<class id>的{​​{1}}似乎每天都在变化。现在它是<img class>,明天它将是其他东西。例如(我缩短了,链接很长):

FFVAD

通过这样说,我需要修复脚本并对<img class="FFVAD" alt="Tag your best friend" decoding="auto" style="" sizes="293px" src="https://scontent-lax3-2.cdninstagram.com/vp/0436c00a3ac9428b2b8c977b45abd022/5BAB3EBC/t51.2885-15/s640x640/sh0.08/e35/33110483_592294374461447_8669459880035221504_n.jpg">进行硬编码,以便能够抓取网页。

Class ID

有人告诉我,我可以使用var = driver.find_elements_by_class_name('FFVAD')查找img.get_attribute('class')并将其存储以供日后使用。但我仍然不明白如何实现这一目标,所以硒或汤可以从class ID抓取Class ID并稍后存储或解析。

我现在得到的就是这个。它有点脏,不对,但想法就在那里。

html tag

当我运行它时,我得到了这个输出,因为页面上有3个图像,我得到3x import requests import selenium.webdriver as webdriver url = ('https://www.instagram.com/kitties') driver = webdriver.Firefox() driver.get(url) last_height = driver.execute_script("return document.body.scrollHeight") while True: imgs_dedupe = driver.find_elements_by_class_name('FFVAD') for img in imgs_dedupe: posts = img.get_attribute('class') print posts driver.execute_script("window.scrollTo(0, document.body.scrollHeight);") time.sleep(scroll_delay) new_height = driver.execute_script("return document.body.scrollHeight") if new_height == last_height: break last_height = new_height

Class ID

2 个答案:

答案 0 :(得分:2)

您目前正在通过硬编码的类名搜索该元素。

如果类名是随机的,则不能再对其进行硬编码。你必须要么:

  • 按其他一些特征搜索元素(例如元素层次结构,其他一些属性等; XPath可以这样做)

    In [10]: driver.find_elements_by_xpath('//article//img')
    Out[10]:
    [<selenium.webdriver.firefox.webelement.FirefoxWebElement (session="1ab4eeb4-10c4-4da4-996c-ee6744445dcc", element="55c48964-8cd0-4472-b35b-214a5a9bfbf7")>,
     <selenium.webdriver.firefox.webelement.FirefoxWebElement (session="1ab4eeb4-10c4-4da4-996c-ee6744445dcc", element="b7f7c8a4-e343-49ca-b416-49f72e67ae07")>,
     <selenium.webdriver.firefox.webelement.FirefoxWebElement (session="1ab4eeb4-10c4-4da4-996c-ee6744445dcc", element="728f6148-6a03-4c9a-9933-36859d65eb51")>]
    
    • 您还可以按元素的视觉特征搜索:尺寸,可见性,位置。这不能完全由XPath完成,但是你必须得到所有<img>标签,并用手工检查每个标签。
      (请参阅下面的示例,因为它很长。)
  • 以某种方式从其他页面逻辑中学习此类名称(如果页面的逻辑本身可以找到并使用它,它必须存在于其他地方,并且必须找到该逻辑通过别的东西等等)

    在这种情况下,类名是renderImage函数中局部变量的一部分,因此它只能通过探索其AST来通过DOM进行挽救。函数本身隐藏在webpack机器内的某处(它似乎将所有资源打包成一些具有单字母名称的全局对象)。或者,您可以将所有包含的JS文件作为原始数据读取,并在其中查找renderImage的定义。因此,在这种情况下,虽然理论上可能仍然存在,但它不成比例地难以实现。

按视觉特征获取元素的示例

在任何页面上,这会找到3张相同尺寸的图片,并排放置(这就是他们在https://www.instagram.com/kitties处的方式)。

由于HTMLElement不能直接传递给Python(至少我找不到任何方法),我们需要传递一些唯一的ID来代替它们,比如独特的XPath。

(JS代码可能更优雅,我对该语言没有多少经验)

In [22]: script = """
  //https://stackoverflow.com/questions/2661818/javascript-get-xpath-of-a-node/43688599#43688599
  function getXPathForElement(element) {
      const idx = (sib, name) => sib 
          ? idx(sib.previousElementSibling, name||sib.localName) + (sib.localName == name)
          : 1;
      const segs = elm => !elm || elm.nodeType !== 1 
          ? ['']
          : elm.id && document.querySelector(`#${elm.id}`) === elm
              ? [`id("${elm.id}")`]
              : [...segs(elm.parentNode), `${elm.localName.toLowerCase()}[${idx(elm)}]`];
      return segs(element).join('/');
  }

  //https://plainjs.com/javascript/styles/get-the-position-of-an-element-relative-to-the-document-24/
  function offsetTop(el){
    return window.pageYOffset + el.getBoundingClientRect().top;
  }

  var expected_images=3;
  var found_groups=new Map();
  for (e of document.getElementsByTagName('img')) {
    let group_id = e.offsetWidth + "x" + e.offsetHeight;
    if (!(found_groups.has(group_id))) found_groups.set(group_id,[]);
    found_groups.get(group_id).push(e);
  }
  for ([k,v] of found_groups) {
    if (v.length != expected_images) {found_groups.delete(k);continue;}
    var offset_top = offsetTop(v[0]);
    for (e of v){
      let _c_oft = offsetTop(e);
      if (_c_oft !== offset_top){
        found_groups.delete(k);
        break;
      }
    }
  }
  if (found_groups.size != 1) {
    console.log(found_groups);
    throw 'Unexpected pattern of images after filtering';
  }

  var found_group = found_groups.values().next().value;


  result=[]
  for (e of found_group) {
    result.push(getXPathForElement(e));
  }
  return result;
"""

In [23]: d.execute_script(script)
Out[23]:
[u'id("react-root")/section[1]/main[1]/div[1]/article[1]/div[1]/div[1]/div[1]/div[1]/a[1]/div[1]/div[1]/img[1]',
 u'id("react-root")/section[1]/main[1]/div[1]/article[1]/div[1]/div[1]/div[1]/div[2]/a[1]/div[1]/div[1]/img[1]',
 u'id("react-root")/section[1]/main[1]/div[1]/article[1]/div[1]/div[1]/div[1]/div[3]/a[1]/div[1]/div[1]/img[1]']

In [27]: [d.find_element_by_xpath(xp) for xp in _]
Out[27]:
[<selenium.webdriver.firefox.webelement.FirefoxWebElement (session="1ab4eeb4-10c4-4da4-996c-ee6744445dcc", element="55c48964-8cd0-4472-b35b-214a5a9bfbf7")>,
 <selenium.webdriver.firefox.webelement.FirefoxWebElement (session="1ab4eeb4-10c4-4da4-996c-ee6744445dcc", element="b7f7c8a4-e343-49ca-b416-49f72e67ae07")>,
 <selenium.webdriver.firefox.webelement.FirefoxWebElement (session="1ab4eeb4-10c4-4da4-996c-ee6744445dcc", element="728f6148-6a03-4c9a-9933-36859d65eb51")>]

答案 1 :(得分:0)

所以我设法使用它(当然在循环之外)

get_img_class = driver.find_elements_by_class_name('img')[1].get_attribute('class')

就像我能够解析Class ID并存储它以供以后使用。非常感谢大家的帮助。所有想法都很棒,并注明以后使用。