从 HideMyAss 抓取 http 代理服务器列表

2015-11-25 15:04:26 +08:00
 ChiChou

HideMyAss 上提供了一个不断更新的匿名代理列表,可以用来给爬虫之类的程序使用。不过为了防止滥用, HideMyAss 随机地在文本中插入不可见元素进行干扰,增加自动抓取匹配难度。

对于前端 🐶 来说,这并没有什么用, jQuery(':not(:visible)').remove() 一句话就能破解。因此最简单的抓取方案便是使用类似 phantom 那样的无界面浏览器,在 DOMContentLoaded 事件中执行 remove 操作,接着就可以愉快地用 innerText 读取单元格里的文本了。

如果觉得 headless browser 的方案太大,可以使用更绿色环保的方式。

在 HMA 中隐藏元素的办法只有两种, style 属性内联样式和 <style> 标签定义干扰文本的 className 。首先找到所有的 style 标签,解析 css 规则,找到设置为不可见(如 display:none )的语句,提取出 Selector 。接着在 DOM 树上执行类似 querySelectorAll 的操作,将集合内的元素节点移除掉。接着查询所有带有 style 属性(内联样式)的元素,同样地,解析 css 规则,判断其是否设置了不可见属性,并根据可见性进行移除操作。

因此我们所需要的依赖项从一个完整的浏览器减少到只需 css parser 和 html parser 。 Python 模块 TinyCSS 只提供基础的 parse 功能,并具体地处理 css 属性,完美符合要求; HTML 的处理就交给 BeautifulSoup4 。

完整实现代码如下:

https://gist.github.com/ChiChou/8ae9512fad468a042c84

#!/usr/bin/env python
#coding:utf-8

'''
  Author: @CodeColorist
  Usage: 
    from hidemyass import proxies
    ...
    for proxy in proxies():
      # do something
'''

import urllib2

import tinycss

from bs4 import BeautifulSoup


def proxies():
  # check whether decl is a css property that hides the element
  isinvisible = lambda decl: (decl.name == 'display' and decl.value.as_css() == 'none') \
    or (decl.name == 'visibility' and decl.value.as_css() == 'hidden')

  css_parser = tinycss.make_parser()
  html = urllib2.urlopen('http://proxylist.hidemyass.com').read()
  soup = BeautifulSoup(html, 'lxml')
  table = soup.find('table', id='listable')
  rows = table.find_all('tr')[1:] # skip first row

  # remove invisible elements
  for style in table.find_all('style'):
    invisible_classes = [rule.selector.as_css() for rule in css_parser.parse_stylesheet(style.text).rules if \
      any(map(isinvisible, rule.declarations))]
    [e.extract() for e in table.select(','.join(invisible_classes))]
    style.extract()

  [e.extract() for e in table.select('[style]') if e.name != 'tr' and any(filter(
    lambda rule: any(map(isinvisible, rule)), css_parser.parse_style_attr(e.get('style'))))]

  # parse data
  order = ('lastupdate', 'ip', 'port', 'country', 'speed', 'conectiontime', 'type', 'anon')

  for tr in rows:
    td = tr.find_all('td')

    yield {key: td[i].div.get('value') \
      if key in ('speed', 'conectiontime') else td[i].text.strip() \
      for i, key in enumerate(order)}


if __name__ == '__main__':
  print list(proxies())
2296 次点击
所在节点    Python
1 条回复
plantparknet
2016-11-22 16:33:52 +08:00
正好用到这部分~~不错呀~~

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/238821

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX