Пример кода и идея следующим образом
while read url; do
wget -q $url -O - | grep -o -E 'href = "([^"#]+)"' | grep "magazine/" | grep "https" | sort -u | sed -r 's/.*href = "([^"]+).*/\1/g' >> list1
perl -ne 'print unless $dup{$_}++;' list1 > list
done < list
Начальная строка списка — https://abc.xyz/issues/, отсюда wget хочет найти одну конкретную URL-ссылку на предыдущую «проблему» точного формата https://abc.xyz/issues/yyyy/mm/dd (фильтровать с помощью grep, удалять дубликаты по сортировке и извлекать URL-ссылку с помощью sed) , затем добавьте URL-ссылку в «список», такая URL-ссылка затем будет использоваться для получения следующей URL-ссылки в цикле «при чтении строки»... и строка Perl хочет удалить дубликаты в списке после появления новой URL-ссылки (s) добавлены в список перед обработкой новой URL-ссылки в цикле.
Такова идея, и идеальным результатом должен быть список сотен ссылок на все прошлые выпуски. Был бы признателен за некоторые предложения или, что лучше, простое решение (у меня есть очень базовые знания команд оболочки)
Если это не сработает, каков ожидаемый результат и что вы получите взамен?
Кажется, что команда perl просто удаляет дубликаты. Разве вы еще не сделали это с sort -u?
Если код работает и вам нужен совет по его улучшению, Code Review — подходящее место. Но сначала посмотрите Руководство по проверке кода для пользователей Stack Overflow.
У вас должна быть возможность объединить все команды: grep -o -E 'href = "https:[^"#]+magazine/[^"#]+"'
@Barmar, это не работает... его запуск немедленно возвращает обратно к приглашению, и без какой-либо новой ссылки в списке... после фильтра grep строка wget иногда все еще возвращает несколько URL-ссылок, а команда perl попробуйте удалить такие дубликаты... спасибо за предложение grep, попробую позже
@Barmar, командная строка работает как для wget, так и для perl, если запустить ее в оболочке; но цикл не работает... во входной <список будет добавлена новая URL-ссылка после каждого цикла, а затем передана команде wget для повторения процесса, чего еще нет
perl -E 'while($u=shift@ARGV){ push @ARGV, grep{!$seen{$_}++ && say} qx{wget -qO- \Q$u\E} =~ m{href = "(https:[^"#]*magazine/[^"#]*)"}g; }' "$url" >list ?
@jhnc эта однострочная команда работает! он останавливается на определенной ссылке после 150+, и оказывается, что на этой конкретной веб-странице отсутствует ссылка href «предыдущая проблема» ... Я плохо представляю, как работает эта однострочная команда, и я хотел бы понять структуру, если вам хочется комментировать:)
@jhnc при повторном использовании приведенной выше команды из ссылки «стоп» я могу вручную указать недостающую ссылку URL, тогда возможно ли, чтобы она следовала существующему «направлению» и получала только ссылку «предыдущая проблема», но не те, которые уже в списке? т.е. /гггг/мм/дд в предыдущих ссылках всегда старше текущей ссылки
на самом деле я не уверен, что мой код работает правильно, поскольку magazine/ не отображается в формате URL, который вы указываете (https://abc.xyz/issues/yyyy/mm/dd), поэтому кажется, что вы ищете его где-то кроме значения href. Вам необходимо предоставить образец HTML-кода из wget.
Разве не должно быть wget -q "$url" ...` ? Кроме того, рекомендуется скопировать/вставить свой код в shellcheck.net и исправить все отмеченные проблемы. Обязательно включите #!/bin/bash (или другой) в качестве первой строки сценария.
@jhnc извините за это, мой образец исследует веб-сайт New Yorker "newyorker.com/magazine", их еженедельные выпуски фактически следуют строгому формату даты +7 в /гггг/мм/дд с 1925 года.





С:
while read url; do
...
perl ... list1 > list
done < list
Вы пишете в тот же файл, что и читаете, list, не делайте этого. Изменять
perl -ne 'print unless $dup{$_}++;' list1 > list
done < list
к
perl -ne 'print unless $dup{$_}++;' list1 > outfile
done < list
или лучше:
perl -ne 'print unless $dup{$_}++;' list1
done < list > outfile
чтобы избежать ЭТОЙ серьезной проблемы, но тогда у вас возникнут и другие проблемы, включая эту:
wget ... >> list1
perl -ne 'print unless $dup{$_}++;' list1
который постоянно добавляется к list1, поэтому perl приходится заново обрабатывать предыдущее содержимое на каждой итерации цикла. Вероятно, вам следует сделать:
wget ... > list1
perl -ne 'print unless $dup{$_}++;' list1
вместо этого заново заполнять list1 на каждой итерации вместо добавления к нему, но я не знаю, почему вы вообще используете там файл, а не просто передаете его по конвейеру perl:
wget ... |
perl -ne 'print unless $dup{$_}++;'
В целом, это может быть то, что вы пытаетесь сделать (непроверено и не рекомендуется, просто пытаетесь заставить код OP функционировать):
#!/usr/bin/env bash
while IFS= read -r url; do
wget -q "$url" -O - |
grep -o -E 'href = "([^"#]+)"' |
grep "magazine/" |
grep "https" |
sort -u |
sed -r 's/.*href = "([^"]+).*/\1/g' |
perl -ne 'print unless $dup{$_}++;'
done < list > outfile
Без примера ввода (вывод из curl) и ожидаемого вывода (из цикла) это всего лишь догадки, но приведенное выше, вероятно, можно было бы изменить на это, чтобы не создавать подоболочки повторно для вызова всех этих команд внутри цикла:
while IFS= read -r url; do
wget -q "$url" -O -
done < list |
grep -o -E 'href = "([^"#]+)"' |
grep "magazine/" |
grep "https" |
sort -u |
sed -r 's/.*href = "([^"]+).*/\1/g' |
perl -ne 'print unless $dup{$_}++;' \
> outfile
и далее я ожидаю, что этот конвейер из 6 команд:
grep -o -E 'href = "([^"#]+)"' |
grep "magazine/" |
grep "https" |
sort -u |
sed -r 's/.*href = "([^"]+).*/\1/g' |
perl -ne 'print unless $dup{$_}++;'
можно заменить одной командой awk (или perl).
Большой! это действительно то, что мне нужно прокормить себя :) недавно упал в эту кроличью нору bash... в моем примере кода, когда я запускаю строку wget, за которой следует строка perl, и получаю новую ссылку URL, я вручную копирую ее в $url и повторите процесс, и я смогу получить ожидаемые URL-ссылки, но не знаю, как передать новый URL-адрес «способом bash» :) это очень помогает!
Кажется, вам нужна структура данных FIFO (первым пришел — первым обслужен), то есть очередь.
Вероятно, вы можете заменить весь свой код одним сценарием Perl.
Немного более легкая для чтения версия кода из моего комментария, использующая промежуточные переменные и измененная, чтобы разрешить возобновление работы из новой очереди:
прогулка_очередь:
#!/usr/bin/perl
use v5.10;
use strict;
use warnings;
use Tie::File;
my ($url_queue_file, $seen_urls_file) = @ARGV;
# allow reading/writing file lines as if they are arrays
tie my @url_queue, 'Tie::File', $url_queue_file or die;
tie my @seen_urls, 'Tie::File', $seen_urls_file or die;
# (re)load lookup table from any previous results found
my %seen = map {$_=>1} @seen_urls;
# take url from head of queue
while (my $url = shift @url_queue) {
# call wget - quote any metacharacters in url
my $html = qx{ wget -qO- \Q$url\E };
# extract href values that are https urls
my @found_urls = ( $html =~ m{href = "(https://[^"#]+)"}g );
# filter out irrelevant and duplicates
my @new_urls = grep { m{magazine/} && !$seen{$_}++ } @found_urls;
# add any new urls to tail of queue
push @url_queue, @new_urls;
# ensure starting url is always marked as seen
unshift @new_urls, $url if !$seen{$url}++;
# append any new urls to result file
push @seen_urls, @new_urls;
# optionally output progress to screen
say for @new_urls;
}
затем:
$ echo 'https://abc.xyz/start' >queue
$ cat /dev/null >found
$ ./walk_queue queue found
queue должен оказаться пустымfound должен содержать результаты (также отображаются на стандартном выводе)Чтобы продолжить с другого URL-адреса, добавьте его в queue, но не удаляйте found, и запустите снова:
$ echo 'https://abc.xyz/another/one' >>queue
$ ./walk_queue queue found
(Я не тестировал этот код, так как не знаю ни фактического формата URL, ни содержимого HTML.)
Большое спасибо! Мне придется некоторое время переваривать код, perl — это буквально слово, которое я искал и установил несколько дней назад с помощью команды, скопированной/вставленной с этого форума, когда я искал ответы:)...интересно одно Команда -line, которую вы предложили ранее, запустите ее дюжину раз, каждый раз подавая новую URL-ссылку, она каждый раз выходит случайным образом из списка результатов, каждый раз, когда она будет следовать по URL-ссылкам «предыдущий/следующий» в обе стороны от начать, для первых нескольких сотен ссылок, затем после этого он «решит» идти только в одном направлении до конца, интересно, как он «решит»?
вероятно, потому, что ссылки с большей вероятностью будут дублироваться, поскольку они выполняются дольше - код комментария не возобновляется; он всегда начинается с пустого «видимого» хеша. вывод должен быть детерминированным для любого конкретного начального URL-адреса
Код работает без каких-либо изменений!... первый запуск завершается через 17 минут 28 секунд в моей студии m1 с 330 ссылками в found, а сейчас второй запуск
Обратите внимание, что очистка веб-сайта может быть недовольна его владельцем. и хотя, возможно, здесь это не проблема, в целом ограничение скорости считается вежливым, даже если парсинг допускается.
согласен... не более чем интерес, контент и код
Вот еще один подход с использованием bash coproc, которому, возможно, немного проще следовать, чем Perl.
прогулка_очередь2:
#!/bin/bash
start_url=$1
seen_urls_file=$2
# (re)load lookup table from previous results found
declare -A seen
if [[ -f $seen_urls_file ]]; then
while IFS= read -r url; do
seen["$url"]=1
done < "$seen_urls_file"
fi
# feed in url, get back zero or more urls from its content
# then append "---" to let us track queue length
coproc extract_urls {
while IFS= read -r url; do
wget -qO- "$url" |
grep -oE 'href = "https://[^"#]+"' |
cut -d'"' -f2 |
grep 'magazine/'
echo ---
done
}
queue_length=0
url=$start_url
while
if (( !seen["$url"]++ )); then
(( ++queue_length ))
echo "$url" >& ${extract_urls[1]}
echo "$url" >> "$seen_urls_file"
echo "$url"
fi
IFS= read -r url
do
while [[ $url = --- ]]; do
(( --queue_length == 0 )) && break 2
IFS= read -r url || break 2
done
done <& ${extract_urls[0]}
Использовать как:
$ cat /dev/null >found
$ ./walk_queue2 'https://abc.xyz/start' found
$ ./walk_queue2 'https://abc.xyz/another/one' found
Приложение: Как обычно, coproc не нужен:
walk_queue3 (то же использование, что и выше):
#!/bin/bash
start_url=$1
seen_urls_file=$2
declare -a queue
qpos=0
queue[qpos]=$start_url
declare -A seen
if [[ -f $seen_urls_file ]]; then
while IFS= read -r url; do
seen["$url"]=1
done < "$seen_urls_file"
fi
while (( ${#queue[@]} > qpos )); do
# for simplicity, we don't delete from queue
url=${queue[qpos++]}
while IFS= read -r new_url; do
if (( !seen["$new_url"]++ )); then
queue+=("$new_url")
echo "$new_url" >> "$seen_urls_file"
echo "$new_url"
fi
done < <(
wget -qO- "$url" |
grep -oE 'href = "https://[^"#]+"' |
cut -d'"' -f2 |
grep 'magazine/'
)
done
Обратите внимание, что замедление, вероятно, будет связано с регулированием со стороны владельца веб-сайта, который может возражать против очистки его веб-сайта.
Дополнительная еда тоже вкусная!
Я не уверен, в чем вопрос. Это работает или нет?