1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/l-weiwei-spiderman

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
Клонировать/Скачать
Внести вклад в разработку кода
Синхронизировать код
Отмена
Подсказка: Поскольку Git не поддерживает пустые директории, создание директории приведёт к созданию пустого файла .keep.
Loading...
README.md

Spiderman — Java-открытый исходный код для веб-данных

Spiderman2 — новейшая предварительная версия уже доступна! Просто, более высокая производительность, сбор данных с сохранением состояния, распределённая система, поддержка JS-скриптов. Не упустите возможность попробовать! PS: стабильные версии будут обновляться здесь позже.

Spiderman представляет собой инструмент для сбора данных из открытых источников на основе Java. Он способен собирать данные с указанных веб-страниц и извлекать из них полезную информацию.

В Spiderman используются такие технологии, как XPath и регулярные выражения.

Он состоит из двух частей:

  • ядро spiderman-core;
  • плагин spiderman-plugin.

Основные характеристики:

  • микроядро + модульная архитектура, гибкость и высокая расширяемость;
  • не требуется писать программный код для извлечения данных;
  • многопоточность обеспечивает высокую производительность.

Как использовать?

  1. Определите целевой сайт и целевую страницу (например, новостную страницу на сайте «новостей»).
  2. Откройте целевую страницу, проанализируйте структуру HTML и получите XPath для нужных данных.
  3. В файле конфигурации XML укажите параметры и запустите Spiderman.

Последние обновления:

  1. Выражение в <parser может инициировать HTTP-запрос для получения содержимого: <parser exp="$Fetcher.get('http://www.baidu.com')".

  2. <target узел добавляет <before узел конфигурации, который работает перед <model узлом. Результаты его работы можно использовать в качестве контекста $before.xxx.

  3. Переработана загрузка данных, поддерживаются различные реализации загрузчиков. Можно настроить собственный загрузчик в файле XML. Официально предоставляются три варианта: на основе HttpClient, на основе WebUnit и на основе Selenium WebDriver. <site downloader="org.eweb4j.spiderman.plugin.util.WebDriverDownloader" или <site downloader="xxx.YourDownloader">

  4. Аналогично пункту 3, переработана обработка модели, теперь поддерживаются разные реализации. Разработчики могут указать свою реализацию в файле XML. На данный момент официально предоставляются две модели: DefaultModelParser и WebDriverModelParser. <before parser="xxx.xxx.xxx.YourModelParser" или <model parser="xxx.YourModelParser".

  5. Другие незначительные обновления, исправления ошибок и т. д.

XPath-получение навыков:

  1. Скачайте плагин xpathonclick.
  2. Установите плагин, перейдите в Chrome-браузер и найдите значок «X Path» в правом верхнем углу.
  3. Откройте нужную страницу в браузере, нажмите на значок в нужном месте на странице (например, заголовок).
  4. Откройте консоль JavaScript (F12), чтобы увидеть XPath.
  5. Помните, что это не окончательный результат, возможно, потребуется дополнительная настройка. Рекомендуется изучить синтаксис XPath.
  6. Изучить синтаксис XPath можно здесь: [http://www.w3school.com.cn/xpath/index.asp].

Пример использования Spiderman:

  • Убедитесь, что ваша машина может запускать Java-программы и выполнять команды Maven.
  • Пример программы: spiderman-sample mvn test.
  • Программа Spiderman будет работать N секунд, после чего данные будут сохранены в папке.
  • Здесь есть статья с примером: [http://my.oschina.net/laiweiwei/blog/100866].

Код для использования Spiderman:

public class TestSpider {

private final Object mutex = new Object();

@Test
public void test() throws Exception {
    String err = EWeb4JConfig.start();
    if (err != null)
        throw new Exception(err);

    SpiderListener listener = new SpiderListenerAdaptor(){
        public void afterScheduleCancel(){
            //调度结束回调
        }
        /**
         * 每次调度执行前回调此方法
         * @date 2013-4-1 下午03:33:11
         * @param theLastTimeScheduledAt 上一次调度时间
         */
        public void beforeEveryScheduleExecute(Date theLastTimeScheduledAt){
            System.err.print("[SPIDERMAN] "+CommonUtil.getNowTime("HH:mm:ss")+" [LAST_SCHEDULE_AT] ~ ");
            System.err.println("at -> " + CommonUtil.formatTime(theLastTimeScheduledAt));
        }
        public void onFetch(Thread thread, Task task, FetchResult result) {
            System.out.print("[SPIDERMAN] "+CommonUtil.getNowTime("HH:mm:ss")+" [FETCH] ~ ");
            System.out.println("fetch result ->" + result + " from -> " + task.sourceUrl);
        }
        public void onNewUrls(Thread thread, Task task, Collection<String> newUrls) {
            System.out.print("[SPIDERMAN] "+CommonUtil.getNowTime("HH:mm:ss")+" [DIG] ~ ");
            System.out.println(newUrls);
        }
        public void onDupRemoval(Thread currentThread, Task task, Collection<Task> validTasks) {

// for (Task t : validTasks){ // System.out.print("[SPIDERMAN] "+CommonUtil.getnowTime("HH:mm:ss")+" [DUPREMOVE] ~ "); // System.out.println(t.url+" from->"+t.sourceUrl); // } } public void onTaskSort(Thread currentThread, Task task, Collection afterSortTasks) { // for (Task t : afterSortTasks){ // System.out.print("[SPIDERMAN]


Обратите внимание, что в тексте запроса присутствуют фрагменты кода, которые не были переведены. Это связано с тем, что они представляют собой программный код, а не текст на естественном языке. ``` +CommonUtil.getNowTime("HH:mm:ss")+ [SORT] ~ ); // System.out.println(t.url+" from->"+t.sourceUrl); // } } public void onNewTasks(Thread thread, Task task, Collection newTasks) { // for (Task t : newTasks){ // System.out.print("[SPIDERMAN] "+CommonUtil.getNowTime("HH:mm:ss")+" [NEWTASK] ~ "); // System.out.println(t.sort + ",,,," + t.url+" from->"+t.sourceUrl); // } } public void onTargetPage(Thread thread, Task task, Page page) { // System.out.print("[SPIDERMAN] "+CommonUtil.getNowTime("HH:mm:ss")+" [TARGET] ~ "); // System.out.println(page.getUrl()); } public void onInfo(Thread thread, Task task, String info) { System.out.print("[SPIDERMAN] "+CommonUtil.getNowTime("HH:mm:ss")+" [INFO] ~ "); System.out.println(info); }

            public void onError(Thread thread, Task task, String err, Throwable e) {
                System.err.print("[SPIDERMAN] "+CommonUtil.getNowTime("HH:mm:ss")+" [ERROR] ~ ");
                e.printStackTrace();
            }
            
            public void onParse(Thread thread, Task task, List<Map<String, Object>> models) {
                final String projectRoot = FileUtil.getTopClassPath(TestSpider.class);
                final File dir = new File(projectRoot+"/Data/"+task.site.getName()+"/"+task.target.getName());
                try {
                    if (!dir.exists())
                        dir.mkdirs();
                    
                    for (int i = 0; i < models.size(); i++) {
                        Map<String, Object> map = models.get(i);
                        String fileName = dir + "/count_" + task.site.counter.getCount() + i;
                        StringBuilder sb = new StringBuilder();
                        for (Iterator<Entry<String,Object>> it = map.entrySet().iterator(); it.hasNext();){
                            Entry<String,Object> e = it.next();
                            boolean isBlank = false;
                            
                            if (e.getValue() == null)
                                isBlank = true;
                            else if (e.getValue() instanceof String && ((String)e.getValue()).trim().length() == 0)
                                isBlank = true;
                            else if (e.getValue() instanceof List && ((ArrayList<?>)e.getValue()).isEmpty())
                                isBlank = true;
                            else if (e.getValue() instanceof List && !((ArrayList<?>)e.getValue()).isEmpty()) {
                                if (((ArrayList<?>)e.getValue()).size() == 1 && String.valueOf(((ArrayList<?>)e.getValue()).get(0)).trim().length() == 0)
                                isBlank = true;
                            }
                                
                            if (isBlank){
                                if (sb.length() > 0)
                                    sb.append("_");
                                sb.append(e.getKey());
                            }
                        }
                        String content = CommonUtil.toJson(map);
                        if (sb.length() > 0)
                            fileName = fileName + "_no_"+sb.toString()+"_";
                        
                        File file = new File(fileName+".json");
                        FileUtil.writeFile(file, content);
                        System.out.print("[SPIDERMAN] "+CommonUtil.getNowTime("HH:mm:ss")+" [INFO] ~ ");
                        System.out.println(fileName + " create finished...");
                    }
                } catch (Exception e) {``` Вот перевод текста на русский язык:

e.printStackTrace(); } } };

        //Запуск паука
        Spiderman.me()
            .init(listener)//Инициализация
            .startup()//Запуск
            .keepStrict("2h");//Время жизни, после истечения времени жизни сразу закрыть
        
        //Запуск паука + планирование перезапуска по расписанию
        //Spiderman.me()
            //.listen(listener)//Установка слушателя
            //.schedule("10s")//Планирование, паук работает 10 секунд
            //.delay("2s")//Каждые 10 + 2 секунды перезапуск паука
            //.times(3)//Планирование 3 раза
            //.startup()//Запуск
            //.blocking();//Блокировка до завершения всех планирований
    }
}

Рассмотрим подробнее файл конфигурации sample:

Сначала есть файл конфигурации инициализации spiderman.properties, который находится в каталоге #{ClassPath}

#Каталог размещения файлов конфигурации веб-сайтов
website.xml.folder=#{ClassPath}/WebSites
#Хранилище каталога уже посещённых URL-адресов веб-сайтов
website.visited.folder=#{ClassPath}/dbEnv
#Количество попыток повторной попытки при неудачном получении данных через HTTP
http.fetch.retry=3
#Тайм-аут соединения HTTP, поддерживает единицы s секунд m минут h часов d дней, если единица не указана, то это s секунд
http.fetch.timeout=5s

Затем в каталоге #{ClassPath}/WebSites есть файл oschina.xml

<?xml version="1.0" encoding="UTF-8"?>
<!--
  | Spiderman Java с открытым исходным кодом вертикальный сетевой паук 
  | Главная страница проекта: https://gitcafe.com/laiweiwei/Spiderman
  | Автор: l.weiwei@163.com
  | Блог: http://laiweiweihi.iteye.com,http://my.oschina.net/laiweiwei
  | QQ: 493781187
  | Электронная почта: l.weiwei@163.com
  | Создание: 2013-01-08 16:12
  | Обновление: 2013-04-10 18:06
-->
<beans>
    <!--
      | name:имя
      | url:начальный URL
      | skipStatusCode:установить, какие коды состояния необходимо игнорировать, несколько кодов разделяются запятыми
      | userAgent:установить идентификатор паука
      | includeHttps:0|1 следует ли извлекать страницы HTTPS
      | isDupRemovalStrict:0|1 строго ли удалять повторяющиеся TargetUrl, т.е. если TargetUrl был посещён один раз, он больше не будет посещаться, даже если он повторяется, пока его источник URL отличается, он будет посещён
      | isFollowRedirects:0|1 следовать ли редиректам 30X для продолжения извлечения
      | reqDelay:{n}s|{n}m|{n}h|n задержка перед каждым запросом
      | enable:0|1 включить ли извлечение этого сайта
      | charset:набор символов сайта
      | schedule:планирование времени, повторное извлечение начального URL каждые {время}
      | thread:количество потоков, выделенных этому сайту паука
      | waitQueue:пауку ждать {время}, прежде чем запрашивать задачи, когда очередь задач пуста
      | timeout:тайм-аут HTTP-запроса
    -->
    <site name="oschina" includeHttps="1" url="http://www.oschina.net/question?catalog=1&amp;show=&amp;p=1" reqDelay="1s" enable="0" charset="utf-8" schedule="1h" thread="2" waitQueue="10s">
        <!--
          | Настройка нескольких начальных URL
          | name:начальный URL
          | url:начальный URL
        -->
        <!--seeds>
            <seed name="" url="" />
        </seeds-->
        <!--
          | Сообщить пауку извлекать только следующие URL хоста, в основном для обработки вторичных или многоуровневых доменных имён
        -->
        <!--validHosts>
            <validHost value="demo.eweb4j.org" />
            <validHost value="wwww.eweb4j.org" />
        </validHosts-->
        <!--
          | Заголовок HTTP
        <headers>
            <header name="" value="" />
        </headers>-->
        <!--
          | Cookie HTTP
        <cookies>
            <cookie name="" value="" host="" path="" />
        </cookies>-->
        <!--
          | Правила добавления URL в очередь
          | policy:стратегия нескольких правил, and | or
        -->
        <queueRules policy="and">
            <!--
              | Правило
              | type:тип правила, включая regex | equal | start | end | contains все правила могут быть добавлены "!" перед ними для отрицания
              | value:значение
            -->
            <rule type="!regex" value="^.*\.(jpg|png|gif)$" />
        </queueRules>
        <!--
          | Извлекаемые цели
        -->
        <targets>
            <!--
              | Ограничить источник целевых URL, обычно это страница канала сайта, например, список новостей в определённой категории
            -->
            <sourceRules policy="and">
                <rule type="regex" value="http://www\.oschina\.net/question\?catalog=1&amp;show=&amp;p=\d+">
                    <!--
                      | Определить, как выкопать новые URL на исходной странице
                      | Этот узел имеет ту же структуру, что и узел <model>, за исключением того, что имя называется не model, а digUrls
                    -->
                    <digUrls>
                        <field name="page_url" isArray="1">
                            <parsers>
                                <parser xpath="//div[@class='QuestionList']//ul[@class='pager']//li[@class='page']//a[@href]" attribute="href" /> xpath="//div[@class='QTitle']/h1/text()"/>
                        </parsers>
                    </field>
                    <field name="content">
                        <parsers>
                            <parser xpath="//div[@class='Content']//div[@class='detail']" exp="$output($this)" />
                            <!--attribute 黑名单-->
                            <parser exp="$Attrs.xml($this).rm('class').rm('style').rm('width').rm('height').rm('usemap').rm('align').rm('border').rm('title').rm('alt').ok()" />
                            <!--tag 黑名лан,去掉内嵌内容-->
                            <parser exp="$Tags.xml($this).rm('map').rm('iframe').rm('object').empty().ok()" />
                            <!--tag 白名单,保留的标签,除此之外都要删除(不删除其他标签内嵌内容)-->
                            <parser exp="$Tags.xml($this).kp('br').kp('h1').kp('h2').kp('h3').kp('h4').kp('h5').kp('h6').kp('table').kp('th').kp('tr').kp('td').kp('img').kp('p').kp('a').kp('ul').kp('ol').kp('li').kp('td').kp('em').kp('i').kp('u').kp('er').kp('b').kp('strong').ok()" />
                            <!--其他-->
                        </parsers>
                    </field>
                    <field name="author">
                        <parsers>
                            <parser xpath="//div[@class='stat']//a[@target='_blank']/text()"/>
                        </parsers>
                    </field>
                    <field name="tags" isArray="1">
                        <parsers>
                            <parser xpath="//div[@class='Tags']//a/text()"/>
                        </parsers>
                    </field>
                    <field name="answers" isArray="1">
                        <parsers>
                            <parser xpath="//li[@class='Answer']//div[@class='detail']/text()" />
                        </parsers>
                    </field>
                </model>
            </target>
        </targets>
        <!--
          | 插件
        -->
        <plugins>
            <!--
              | enable:是否开启
              | name:插件名
              | version:插件版本
              | desc:插件描述
            -->
            <plugin enable="1" name="spider_plugin" version="0.0.1" desc="这是一个官方实现的默认插件,实现了所有扩展点。">
                <!--
                  | 每个插件包含了对若干扩展点的实现
                -->
                <extensions>
                    <!--
                      | point:扩展点名它们包括  task_poll, begin, fetch, dig, dup_removal, task_sort, task_push, target, parse, pojo, end
                    -->
                    <extension point="task_poll">
                        <!--
                          | 扩展点实现类
                          | type: 如何获取实现类 ,默认通过无参构造器实例化给定的类名,可以设置为ioc,这样就会从EWeb4J的IOC容器里获取
                          | value: 当时type=ioc的时候填写IOC的bean_id,否则填写完整类名
                          | sort: 排序,同一个扩展点有多个实现类,这些实现类会以责任链的方式进行执行,因此它们的执行顺序将变得很重要
                        -->
                        <impl type="" value="org.eweb4j.spiderman.plugin.impl.TaskPollPointImpl" sort="0"/>
                    </extension>
                    <extension point="begin">
                        <impl type="" value="org.eweb4j.spiderman.plugin.impl.BeginPointImpl" sort="0"/>
                    </extension>
                    <extension point="fetch">
                        <impl type="" value="org.eweb4j.spiderman.plugin.impl.FetchPointImpl" sort="0"/>
                    </extension>
                    <extension point="dig">
                        <impl type="" value="org.eweb4j.spiderman.plugin.impl.DigPointImpl" sort="0"/>
                    </extension>
                    <extension point="dup_removal">
                        <impl type="" value="org.eweb4j.spiderman.plugin.impl.DupRemovalPointImpl" sort="0"/>
                    </extension> *Расширение точки task_sort:*

*impl type: «» value: «org.eweb4j.spiderman.plugin.impl.TaskSortPointImpl» sort: «0».*

Расширение точки task_push:

impl type: «» value: «org.eweb4j.spiderman.plugin.impl.TaskPushPointImpl» sort: «0».

Расширение точки target:

impl type: «» value: «org.eweb4j.spiderman.plugin.impl.TargetPointImpl» sort: «0».

Расширение точки parse:

impl type: «» value: «org.eweb4j.spiderman.plugin.impl.ParsePointImpl» sort: «0».

Расширение точки end:

impl type: «» value: «org.eweb4j.spiderman.plugin.impl.EndPointImpl» sort: «0».

Провайдеры:

провайдер:

организация name: «CFuture» website: «http://lurencun.com» desc: «Color your future»:

 *автор name: «weiwei» website: «http://laiweiweihi.iteye.com | http://my.oschina.net/laiweiwei» email: «l.weiwei@163.com» weibo: «http://weibo.com/weiweimiss» desc: «один IT-парень, который любит свободу, музыку и рисование».*

Плагин:

сайт:

бобы:

плагины:

Введение

Мощная Java-программа для сканирования, постраничная навигация по спискам и деталям, AJAX, микроядро с высокой расширяемостью, гибкая конфигурация. Развернуть Свернуть
Apache-2.0
Отмена

Обновления

Пока нет обновлений

Участники

все

Недавние действия

Загрузить больше
Больше нет результатов для загрузки
1
https://api.gitlife.ru/oschina-mirror/l-weiwei-spiderman.git
git@api.gitlife.ru:oschina-mirror/l-weiwei-spiderman.git
oschina-mirror
l-weiwei-spiderman
l-weiwei-spiderman
master