我曾想过尝试编写一个简单的爬虫,它可能会爬取并为我们的 NPO 的网站和内容生成其发现的列表。
有人对如何做到这一点有任何想法吗?您将爬虫指向哪里开始?它如何发回其发现并继续爬行?它怎么知道它发现了什么,等等。
我曾想过尝试编写一个简单的爬虫,它可能会爬取并为我们的 NPO 的网站和内容生成其发现的列表。
有人对如何做到这一点有任何想法吗?您将爬虫指向哪里开始?它如何发回其发现并继续爬行?它怎么知道它发现了什么,等等。
可以肯定的是,您将重新发明轮子。但这里是基础:
将它们放在持久存储中,这样您就可以停止和启动爬虫而不会丢失状态。
算法是:
while(list of unvisited URLs is not empty) {
take URL from list
remove it from the unvisited list and add it to the visited list
fetch content
record whatever it is you want to about the content
if content is HTML {
parse out URLs from links
foreach URL {
if it matches your rules
and it's not already in either the visited or unvisited list
add it to the unvisited list
}
}
}
爬虫的复杂部分是如果你想将它扩展到大量的网站/请求。在这种情况下,您将不得不处理一些问题,例如:
不可能将信息全部保存在一个数据库中。
没有足够的 RAM 来处理巨大的索引
多线程性能和并发性
爬虫陷阱(通过更改 url、日历、会话 id ... 创建的无限循环)和重复的内容。
从多台计算机上爬网
格式错误的 HTML 代码
来自服务器的持续 http 错误
没有压缩的数据库,这使您对空间的需求增加了大约 8 倍。
重新抓取例程和优先事项。
使用压缩请求(Deflate/gzip)(适用于任何类型的爬虫)。
还有一些重要的事情
尊重 robots.txt
并且每个请求的爬虫延迟都不会使网络服务器窒息。
多线程网络爬虫
如果你想爬取大型网站,那么你应该编写一个多线程爬虫。在文件/数据库中连接、获取和写入爬取的信息 - 这些是爬取的三个步骤,但如果您使用单线程,那么您的 CPU 和网络利用率将会下降。
一个多线程的网络爬虫需要两个数据结构——linksVisited(这应该实现为 hashmap 或 trai)和 linksToBeVisited(这是一个队列)。
网络爬虫使用 BFS 遍历万维网。
基本网络爬虫的算法:-
重复步骤 2 到 5,直到队列为 linksToBeVisited 为空。
这是有关如何同步线程的代码片段......
public void add(String site) {
synchronized (this) {
if (!linksVisited.contains(site)) {
linksToBeVisited.add(site);
}
}
}
public String next() {
if (linksToBeVisited.size() == 0) {
return null;
}
synchronized (this) {
// Need to check again if size has changed
if (linksToBeVisited.size() > 0) {
String s = linksToBeVisited.get(0);
linksToBeVisited.remove(0);
linksVisited.add(s);
return s;
}
return null;
}
}
爬虫的概念很简单。
您通过 HTTP GET 获取根页面,对其进行解析以查找 URL 并将它们放在队列中,除非它们已经被解析(因此您需要已解析页面的全局记录)。
您可以使用 Content-type 标头找出内容的类型,并将您的爬虫限制为仅解析 HTML 类型。
您可以去掉 HTML 标签以获取纯文本,您可以对其进行文本分析(以获取标签等,页面的内容)。如果您掌握了高级功能,您甚至可以在图像的 alt/title 标签上执行此操作。
在后台,您可以有一个线程池从队列中获取 URL 并执行相同的操作。您当然想限制线程数。
如果您的 NPO 的网站相对较大或复杂(具有可有效创建“黑洞”的动态页面,例如带有“第二天”链接的日历),您最好使用真正的网络爬虫,如Heritrix。
如果站点总共有几页,您可以只使用 curl 或 wget 或您自己的。请记住,如果它们开始变大,或者您开始使脚本变得更复杂以使用真正的爬虫,或者至少查看其源代码以了解它们在做什么以及为什么。
一些问题(还有更多):
您可以制作一个单词列表,并为在 google 搜索的每个单词创建一个线程。
然后每个线程将为它在页面中找到的每个链接创建一个新线程。
每个线程都应该写入它在数据库中找到的内容。当每个线程完成读取页面时,它就会终止。
你的数据库中有一个非常大的链接数据库。
使用 wget,做一个递归的网络吸吮,它将所有文件转储到你的硬盘上,然后编写另一个脚本来检查所有下载的文件并分析它们。
编辑:或者可能是 curl 而不是 wget,但我不熟悉 curl,我不知道它是否像 wget 那样进行递归下载。
我在.net 中使用响应式扩展做了一个简单的网络爬虫。
https://github.com/Misterhex/WebCrawler
public class Crawler
{
class ReceivingCrawledUri : ObservableBase<Uri>
{
public int _numberOfLinksLeft = 0;
private ReplaySubject<Uri> _subject = new ReplaySubject<Uri>();
private Uri _rootUri;
private IEnumerable<IUriFilter> _filters;
public ReceivingCrawledUri(Uri uri)
: this(uri, Enumerable.Empty<IUriFilter>().ToArray())
{ }
public ReceivingCrawledUri(Uri uri, params IUriFilter[] filters)
{
_filters = filters;
CrawlAsync(uri).Start();
}
protected override IDisposable SubscribeCore(IObserver<Uri> observer)
{
return _subject.Subscribe(observer);
}
private async Task CrawlAsync(Uri uri)
{
using (HttpClient client = new HttpClient() { Timeout = TimeSpan.FromMinutes(1) })
{
IEnumerable<Uri> result = new List<Uri>();
try
{
string html = await client.GetStringAsync(uri);
result = CQ.Create(html)["a"].Select(i => i.Attributes["href"]).SafeSelect(i => new Uri(i));
result = Filter(result, _filters.ToArray());
result.ToList().ForEach(async i =>
{
Interlocked.Increment(ref _numberOfLinksLeft);
_subject.OnNext(i);
await CrawlAsync(i);
});
}
catch
{ }
if (Interlocked.Decrement(ref _numberOfLinksLeft) == 0)
_subject.OnCompleted();
}
}
private static List<Uri> Filter(IEnumerable<Uri> uris, params IUriFilter[] filters)
{
var filtered = uris.ToList();
foreach (var filter in filters.ToList())
{
filtered = filter.Filter(filtered);
}
return filtered;
}
}
public IObservable<Uri> Crawl(Uri uri)
{
return new ReceivingCrawledUri(uri, new ExcludeRootUriFilter(uri), new ExternalUriFilter(uri), new AlreadyVisitedUriFilter());
}
public IObservable<Uri> Crawl(Uri uri, params IUriFilter[] filters)
{
return new ReceivingCrawledUri(uri, filters);
}
}
您可以按如下方式使用它:
Crawler crawler = new Crawler();
IObservable observable = crawler.Crawl(new Uri("http://www.codinghorror.com/"));
observable.Subscribe(onNext: Console.WriteLine,
onCompleted: () => Console.WriteLine("Crawling completed"));
I'm using Open search server for my company internal search, try this : http://open-search-server.com its also open soruce.