爬虫
网络爬虫,是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本。另外一些不常使用的名字还有蚂蚁、自动索引、模拟程序或者蠕虫。
爬虫分类
- 通用
- 聚焦
- 增量式
- 深层网络
网页去重
- simhash
海明距离
在信息编码中,两个合法代码对应位上编码不同的位数称为码距,又称海明距离。举例如下:10101和00110从第一位开始依次有第一位、第四、第五位不同,则海明距离为3
运行方式
- 起始爬行页面集合:通过手动指定或者自动化的方式,是爬虫的种子列表
- 从起始爬行页面提取链接,对链接规范化,处理掉链接的别名(如url里的hash),并将相对链接转为绝对链接,加入爬行列表
- 减少爬取环路,使用哈希表、布隆过滤器等过滤爬取过的页面,并且要将这些数据持久化
- 由于诸如CGI以及文件系统的符号链接等的存在,环路是无法完全避免的
环路减少策略
- 规范化URL 避免别名
- 广度优先爬行,避免无限深入某一URL链
- 对每个站点进行流量控制,避免爬取的太多
- 限制URL的长度
- 人工监视,维护URL环路黑名单
- 对URL进行模式检测,发现可能是回环的URL
- 内容指纹,对内容进行取数字摘要
robots.txt
版本 | 说明 |
---|---|
0.0 | 支持Disallow指令 禁止爬行 |
1.0 | 支持Allow指令 允许爬行 |
2.0 | 新增正则表达式及定时功能 |
格式
# 注释: 禁止百度蜘蛛和谷歌蜘蛛爬取/adminUser-Agent: baidu-spiderUser-Agent: google-spiderDisallow: /admin# 禁止所有蜘蛛爬取/super-adminUser-Agent: *Disallow: /super-admin
url的匹配是前缀匹配
爬虫META指令
<meta name='robots' content='noindex'>
content可取值如下:
- noindex 禁止索引本页面
- nofollow 禁止爬取本页面的外链
- index 可以对本页面索引
- follow 可以爬取本页面的所有外链
- all 等价于 index+follow
- none 等价于 noindex+nofollow
给搜索引擎的META标签
<meta name='description' content="页面描述"><meta name='keywords' content="关键词1, 关键词2"><!-- 10天后再来爬一次 --><meta name='revisit-after' content="10 days">
使用Java爬
HttpClient
Get请求
HttpGet get = new HttpGet("http://www.baidu.com");try (CloseableHttpClient client = HttpClients.createDefault(); CloseableHttpResponse response = client.execute(get)) { String s = EntityUtils.toString(response.getEntity(), "utf8"); System.out.println(s);}
- 设置参数
URIBuilder uriBuilder = new URIBuilder("http://www.baidu.com/s").addParameter("wd", "关键词");HttpGet get = new HttpGet(uriBuilder.build());
POST请求
var request = new HttpPost("http://example");var pairs = List.of(new BasicNameValuePair("keys", "java"), new BasicNameValuePair("keys", "python"));UrlEncodedFormEntity entity = new UrlEncodedFormEntity(pairs, "utf8");request.setEntity(entity);
连接池
PoolingHttpClientConnectionManager manager = new PoolingHttpClientConnectionManager();// 最大连接数manager.setMaxTotal(100);// 每个主机最大连接数manager.setDefaultMaxPerRoute(10);CloseableHttpClient client = HttpClients.custom().setConnectionManager(manager).build();
请求参数
RequestConfig config = RequestConfig.custom().setConnectTimeout(1000) // 获取连接的最长时间,单位ms .setConnectionRequestTimeout(500) // 获取连接的最长时间 .setSocketTimeout(10000).build(); // 传输数据最长时间HttpGet get = new HttpGet();get.setConfig(config);
Jsoup
DOM
Document doc = Jsoup.parse(new URL("http://www.baidu.com"), 10000);doc.getElementByXXX();
元素API
Element e = doc.getElementById("login"); System.out.println(e.id()); System.out.println(e.className()); System.out.println(e.classNames()); System.out.println(e.text()); System.out.println(e.attr("style")); System.out.println(e.attributes());
使用CSS选择器
doc.select("div") .forEach(System.out::println);
WebMagic
使用
- 实现PageProcessor
public class JobProcessor implements PageProcessor { private Site site = Site.me().addHeader("User-Agent","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"); @Override public void process(Page page) { page.putField("div1",page.getHtml().css("#logo-2014").get()); page.putField("div2",page.getHtml().xpath("//div").get()); } @Override public Site getSite() { return site; }}
- 运行
Spider.create(new JobProcessor()) .addUrl("https://search.jd.com/Search?keyword=%E6%89%8B%E6%9C%BA&enc=utf-8&wq=%E6%89%8B%E6%9C%BA&pvid=9524dc8e0e2d45f08656f023fa60a0de") .run();
元素抽取
- XPath
- Regex
- CSS
page.putField("div1",page.getHtml().css("#logo-2014").get()); // 获取一条 page.putField("div2",page.getHtml().xpath("//div[@id=logo-2014]").all()); // 获取全部
获取链接
page.getHtml().links()
- 递归访问
page.addTargetRequests(page.getHtml().links().all());
输出数据
- PipeLine
Spider.create(new JobProcessor()) .addUrl("url") .addPipeline(new FilePipeline("./result.txt")) .run();
Site
Scheduler
- 使用布隆过滤器
Spider.create(null) .setScheduler(new QueueScheduler().setDuplicateRemover(new BloomFilterDuplicateRemover(100000)));
三种去重方式
- HashSet
- Redis
- 布隆过滤器
文档
使用代理
downloader.setProxyProvider(new SimpleProxyProvider(List.of(new Proxy("116.114.19.211",443))));Spider.create(new ProxyProcessor()) .addUrl("http://ip-api.com/json/?lang=zh-CN") .setDownloader(downloader) .run();