如何写一个爬虫?(How to write a crawler?)

2019-06-23 20:20发布

我曾试图写一个简单的爬虫可能抓取并产生及其对我们的NPO的网站和内容的调查结果列表的想法。

有谁知道如何做到这一点有什么想法? 你在哪里指向履带开始了吗? 它是如何发回它的发现,仍然保持爬行? 它怎么知道什么发现,等等,等等。

Answer 1:

你会被重新发明轮子,以确保万无一失。 但这里的基本知识:

  • 未访问过的网址列表 - 种子这与一个或多个起始页面
  • 访问过的URL列表 - 所以你不要去转转
  • 一种网址的一套规则你不感兴趣 - 所以你不索引整个互联网

把这些持久性存储,这样你就可以停止和启动履带不失状态。

算法是:

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
        }
    }
}


Answer 2:

履带的复杂的部分是,如果你想将它扩展到网站/请求的数量巨大。 在这种情况下,你将不得不处理像一些问题:

  • 不可能让所有的信息在一个数据库中。

  • 没有足够的内存来处理庞大的指数(S)

  • 多线程性能和并发

  • 履带式陷阱(通过改变网址,日历,会议IDS ...创建无限循环)和复制的内容。

  • 从多台计算机抓取

  • 畸形的HTML代码

  • 从服务器持续HTTP错误

  • 无压缩数据库,至极使你的空间约8倍大的需求。

  • 重新抓取程序和优先事项。

  • 采用带压缩(放气/ gzip的)(适用于任何类型的履带式的)请求。

而一些重要的事情

  • 尊敬的robots.txt

  • 并在每个请求履带延迟不受阻的Web服务器。



Answer 3:

多线程网络爬虫

如果你想抓取大型网站,那么你应该写一个多线程的履带。 连接,读取和写入文件中/数据库抓取信息 - 这些都是三个步骤爬行的,但如果你使用一个单线程比你的CPU和网络的利用率就倒。

一种多线程web爬虫需要两个数据结构 - linksVisited和linksToBeVisited(这是一个队列)(这应该作为散列映射或TRAI来实现)。

网络爬虫使用BFS遍历万维网。

一个基本的网络爬虫的算法: -

  1. 添加一个或多个种子URL来linksToBeVisited。 该方法以添加URL以linksToBeVisited必须同步。
  2. 从弹出的linksToBeVisited元素,这增加linksVisited。 这pop方法从linksToBeVisited弹出网址必须同步。
  3. 从互联网上获取的页面。
  4. 解析文件,并添加任何直到在页面linksToBeVisited发现现在还没有访问过的链接。 URL的可以根据需要进行过滤。 用户可以给过滤器一组规则的URL进行扫描哪些。
  5. 在网页上找到所需的信息保存在数据库或文件。
  6. 重复步骤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; } } 



Answer 4:

爬行是在概念上很简单。

你得到通过HTTP根页取得,分析它找到的网址,并把它们放在一个队列,除非他们已经已经被解析(所以你需要的你已经解析页面的全球纪录)。

您可以使用Content-type头找出的内容的类型是什么,并限制爬虫只能解析HTML类型。

您可以剥离出的HTML标签获得纯文本,您可以在(获得标签等,该页面的肉)做文本分析。 你甚至可以做到这一点的ALT / title标签的图片,如果你得到了先进的。

而在后台,你可以有吃的线程从队列中的URL和做同样的一个游泳池。 要限制当然线程数。



Answer 5:

如果您的非营利组织的网站是比较大的或复杂的(具有将有效地创建一个“黑洞”像一个“第二天”链接日历动态页面),你会使用一个真正的网络爬虫,像更好Heritrix的。

如果网站总页面数数你可以逃脱只是使用curl或wget的还是你自己。 请记住,如果他们开始得到大或者你开始做你的脚本更复杂,只需使用一个真正的履带或至少看看它的源代码,看看他们在做什么以及为什么。

有些问题(还有更多):

  • 黑洞(如所描述的)
  • 重试次数(如果你得到一个500?)
  • 重定向
  • 流量控制(否则你可以在网站的负担)
  • robots.txt的实现


Answer 6:

维基百科大约有一篇好文章的网页抓取工具 ,涵盖了很多的算法和注意事项。

不过,我不会刻意写我自己的履带。 这是一个大量的工作,因为你只需要一个“简单的履带式”,我想你真正需要的是一个现成的,货架履带 。 有很多的自由和开放源码抓取工具,这将有可能做你需要的一切,对你的一部分很少的工作。



Answer 7:

你可以做一个单词列表,并在谷歌搜索每个字一个线程。
然后,每个线程将创建为每个链接它在页面找到一个新的线程。
每个线程应该写什么发现在数据库中。 当每个线程完成阅读的页面,它会终止。
有你有在你的数据库链接一个非常大的数据库。



Answer 8:

我使用的是开放式搜索服务器为我公司内部搜索,试试这个: http://open-search-server.com它也是开放soruce。



Answer 9:

使用wget,做一个递归网络吸,将所有文件转储到你的硬盘,然后写另一个脚本要经过所有下载的文件,并对其进行分析。

编辑:也许卷曲的,而不是wget的,但我不熟悉,卷曲,我不知道如果这样做递归下载像wget。



Answer 10:

我没有使用.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"));


文章来源: How to write a crawler?
标签: web-crawler