代码之家  ›  专栏  ›  技术社区  ›  Jason

如何编写爬虫程序?

  •  61
  • Jason  · 技术社区  · 16 年前

    我曾想过尝试写一个简单的爬虫,可能会爬行,并产生一个清单,为我们的非营利组织的网站和内容的调查结果。

    有没有人想到怎么做?你把爬虫指向哪里开始?它是如何发回自己的发现并继续爬行的?它怎么知道它发现了什么。

    10 回复  |  直到 10 年前
        1
  •  147
  •   slim    6 年前

    你肯定会重新发明轮子的。但基本情况如下:

    • 一个未访问的网址列表-种子与一个或多个起始页
    • 一组你不感兴趣的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
            }
        }
    }
    
        2
  •  30
  •   stema Arun Mohan    13 年前

    爬虫程序的复杂部分是如果你想将它扩展到大量的网站/请求。 在这种情况下,您必须处理一些问题,如:

    • 无法将所有信息保存在一个数据库中。

    • 多线程性能和并发性

    • 爬虫陷阱(通过更改URL、日历、会话ID等创建的无限循环)和重复内容。

    • 从多台计算机爬网

    • 格式错误的HTML代码

    • 来自服务器的持续http错误

    • 重新制定程序和优先顺序。

    • 使用压缩请求(Deflate/gzip)(适用于任何类型的爬虫程序)。

    还有一些重要的事情

    • 尊重机器人.txt

    • 每个请求上都有一个爬虫延迟,以避免阻塞web服务器。

        3
  •  8
  •   kleopatra Aji kattacherry    12 年前

    多线程网络爬虫

    如果你想爬网大型网站,那么你应该写一个多线程爬虫。 连接、获取和写入文件/数据库中的爬网信息-这是爬网的三个步骤,但如果使用单线程,则CPU和网络利用率将大大降低。

    一个多线程的网络爬虫需要两个数据结构-linksVisited(这应该实现为hashmap或trai)和linkstobevisted(这是一个队列)。

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

    基本web的算法爬虫:-

    1. 从internet获取页面。
    2. 解析文件并将页面中找到的任何到目前为止未访问的链接添加到linkstobevisted。如果需要,可以过滤URL。用户可以提供一组规则来过滤要扫描的url。
    3. 在页面上找到的必要信息保存在数据库或文件中。
    4. 重复步骤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;
           }
        }
      

        4
  •  5
  •   JeeBee    16 年前

    爬虫的概念很简单。

    您通过httpget获得一个根页面,对其进行解析以找到url并将其放入队列,除非它们已经被解析(因此您需要一个已经解析的页面的全局记录)。

    您可以使用contenttypeheader找出内容的类型,并将爬虫程序限制为只解析HTML类型。

    您可以去掉HTML标记以获得纯文本,您可以对纯文本进行文本分析(以获得标记等页面的内容)。你甚至可以在图片的alt/title标签上这样做。

    在后台,您可以有一个线程池,从队列中获取url并执行相同的操作。当然,你要限制线程的数量。

        5
  •  5
  •   Vinko Vrsalovic    16 年前

    如果你的NPO的网站比较大或者比较复杂(有动态页面,可以有效地创建一个“黑洞”,比如带有“第二天”链接的日历),你最好使用一个真正的网络爬虫,比如 Heritrix.

    如果这些站点总共有几个页面,那么只需使用curl、wget或您自己的页面就可以了。只要记住,如果他们开始变大,或者你开始让你的脚本更复杂,只使用一个真正的爬虫,或者至少看看它的来源,看看他们在做什么,为什么。

    一些问题(还有更多):

    • 重定向
    • 流量控制(否则你会成为网站的负担)
    • 机器人.txt实施
        6
  •  4
  •   Derek Park    16 年前

    web crawlers ,涵盖了许多算法和注意事项。

    off-the-shelf crawler . 有很多免费和开源的爬虫程序,可能会做你需要的一切,而你的工作很少。

        7
  •  2
  •   Gero    16 年前

    你可以做一个单词列表,并为谷歌搜索到的每个单词创建一个线程。


    你的数据库里有一个很大的链接数据库。

        8
  •  0
  •   whatsisname    16 年前

    编辑:或者用curl代替wget,但是我不熟悉curl,我不知道它是否像wget那样进行递归下载。

        9
  •  0
  •   Sathishkumar    12 年前

    我正在使用Open search server进行公司内部搜索,请尝试以下操作: http://open-search-server.com 它也是开放式的。

        10
  •  0
  •   Misterhex    11 年前

    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"));