여러 파일의 img html 태그에서 URL 추출

여러 파일의 img html 태그에서 URL 추출

<p>..</p>모두 html 태그가 있고 <img...>모든 파일에 html 태그가 있는 많은 .html 파일이 있는 폴더가 있습니다.

추출하려고 하는데URL만 <img...>HTML에서 발견현재 폴더의 모든 파일을 하나의 output.txt파일 로 표시

html 파일 코드는 다음과 같습니다.

file1.html
<p> text...</p>
<img style='display' src="https://www.example.com/image1.jpg" width="100px" alt="image1"/>
<p> text...</p>
<p> text.. <a href="https://www.example.com/1">1</a> ... text....</p>
file2.html
<p> text...</p>
<img style='display' src="https://www.example.com/image2.jpg" width="100px" alt="image2"/>
<p> text...</p>
<p> text.. <a href="https://www.example.com/2">2</a> ... text....</p>

이미지 URL 경로에는 특정 패턴이 없으며 URL 경로에 무엇이든 포함될 수 있습니다.

<img ...>내 문제는 이러한 html 파일 중 일부에 html 태그 내부 외에 다른 URL이 있을 수 있다는 것입니다. 따라서 html 태그에서 추출하기 만 하면 됩니다 .

<img ...>이상적으로는 html 태그 내에서 발견된 모든 URL을 다음과 같이 output.txt로 추출 하려고 합니다 .

output.txt
https://www.example.com/image1.jpg
https://www.example.com/image2.jpg
etc

sed 또는 Regex를 사용하여 이를 달성할 수 있습니까?

이 sed 명령을 사용해 보았지만 URL이 어디에 있든 모든 URL을 추출하는 것 같습니다.

sed -n 's#.*\(https*://[^"]*\).*#\1#;p' file

감사합니다!

답변1

진짜정규식 기반 추출 방법을 sed사용 하고 싶지 않습니다 . grepHTML은 구조화된 텍스트이므로 해당 텍스트에서 데이터를 안정적으로 추출하려면 HTML 파서가 필요합니다.

C, go, Rust, java, python, php, perl 등을 포함한 대부분의 언어에 사용할 수 있는 HTML 구문 분석 라이브러리가 있습니다. 쉘 스크립트에서 HTML/XHTML/XML 파일을 구문 분석하고 처리하기 위한 명령줄 도구도 있습니다. xml_grepxmlstarlet도구는 훌륭하지만 내 경험상 입력 파일이 사양을 준수하도록 요구하는 데 더 엄격한 경향이 있습니다. 특히 HTML의 경우 이는 문제를 일으킬 수 있습니다. 사양을 엄격히 준수하는 것은 실제 웹사이트에서는 흔하지 않습니다("HTML 파일은 일반적으로 쓰레기입니다"라고 정중하게 말하는 방식입니다). 구문 분석 라이브러리는 처리할 내용에 대해 더 관대한 경향이 있으며 더 엄격한 도구에서 거부되는 입력을 쉽게 처리할 수 있습니다.

그런데 , 구조화된 텍스트를 grep, sed, cut 등과 같은 줄 기반 도구를 사용하여 더 쉽게 처리할 수 있는 줄 기반 형식으로 변환하는 도구 xml2도 있습니다.html2

그럼에도 불구하고 HTML 파서를 사용하는 것은 정규식을 사용하는 것보다 더 안정적일 뿐만 아니라 일반적으로 더 쉽습니다.

Perl을 사용한 예는 다음과 같습니다.HTML::TokeParser::단순파서, 간단한 인터페이스HTML::파서기준 치수. 일반적인 Linux 배포판을 실행하고 있다면 거의 확실하게 패키지로 제공됩니다. 예를 들어 Debian 및 그 파생 버전에서는 libhtml-tokeparser-simple-perllibhtml-parser-perl. 그렇지 않으면 사용할 수 있습니다 cpan.

$ cat extract-img-urls.pl 
#!/usr/bin/perl

use strict;
use v5.16;   # for fc (fold case) function

use HTML::TokeParser::Simple;

foreach my $f (@ARGV) {
  my $p = HTML::TokeParser::Simple->new(file => $f);
  while (my $token = $p->get_token) {
    next unless fc($token->[1]) eq fc('img');
    print $token->[2]->{src} . "\n";
  }
};

파일에 저장하고 chmod를 사용하여 실행 가능하게 만듭니다. 예:chmod +x ./extract-img-urls.pl

인수로 처리할 HTML 파일 목록을 사용하여 실행하세요. 이 작업을 수동으로 수행하거나 및 find와 같은 것을 사용하여 -exec파일 이름 목록을 제공할 수 있습니다. 예를 들어 다음은 IMG SRC URL이 두 개만 있고 index.html둘 다 상대적이라는 것을 보여줍니다.

$ find ~/public_html/ -maxdepth 1 -type f -name 'index.html' -exec ./extract-img-urls.pl {} +
cas.jpg
valid-html401.png

분명히 find단일 디렉터리의 단일 파일을 일치시키는 데 사용하는 것은 과잉입니다. 이것은 잘 작동하지만... ./extract-img-urls.pl ~/public_html/index.html여러 하위 디렉터리에 있는 여러 파일에 대한 좋은 예는 아닙니다.

-name '*.html'귀하의 경우에는 (또는 -iname '*.html'대소 문자를 구분하지 않는 일치) 를 사용하여 실행할 수 있습니다 . -maxdepth 1하위 디렉터리에서도 .html 파일을 찾을 수 있도록 조건자를 제거할 수도 있습니다 .

find ~/public_html/ -type f -iname '*.html' -exec ./extract-img-urls.pl {} +

마지막으로 이 예에서는 Perl 스크립트가 현재 디렉터리에 있다고 가정합니다. 그렇지 않은 경우 대신 실제 경로를 지정하십시오 ./. 또는 $PATH 어딘가에 있는 경우(예: 디렉토리 /usr/local/bin/를 생성~/bin/


IMG SRC URL은 일반적으로 상대 URL입니다. dirname()다음 함수를 사용하여 (매우 간단하게) 각 상대 이미지 파일 이름에 대한 기본 디렉터리를 추가합니다.파일::기본 이름모듈(Perl에 포함된 핵심 Perl 모듈):

#!/usr/bin/perl

use strict;
use v5.16;   # for fc (fold case) function

use HTML::TokeParser::Simple;
use File::Basename;

foreach my $f (@ARGV) {
  my $base = dirname($f);
  my $p = HTML::TokeParser::Simple->new(file => $f);
  while (my $token = $p->get_token) {
    next unless fc($token->[1]) eq fc('img');
    if ($token->[2]->{src} =~ m=^(https?|ftp)://|^/=i) {
      print $token->[2]->{src} . "\n";
    } else {
      print $base . "/" . $token->[2]->{src} . "\n";
    }
  }
};

산출:

$ find ~/public_html/ -maxdepth 1 -type f -name 'index.html' -exec ./extract-img-urls.pl {} +
/home/cas/public_html/cas.jpg
/home/cas/public_html/valid-html401.png

마지막으로 이 버전은 처리하는 각 파일의 이름을 인쇄하며, 각 IMG SRC URL 앞에 탭 문자를 삽입하고 \n각 파일 뒤에 줄바꿈( )을 삽입합니다. 다른 도구나 스크립트를 사용하여 출력을 처리하려는 경우 개행 구분 기호가 유용합니다. 많은 도구/언어에는 "단락 모드"에서 데이터를 읽을 수 있는 옵션이 있으므로 이러한 텍스트를 처리하는 것은 쉽습니다.

#!/usr/bin/perl

use strict;
use v5.16;   # for fc (fold case) function

use HTML::TokeParser::Simple;
use File::Basename;

foreach my $f (@ARGV) {
  print "$f\n";
  my $base = dirname($f);
  my $p = HTML::TokeParser::Simple->new(file => $f);
  while (my $token = $p->get_token) {
    next unless fc($token->[1]) eq fc('img');
    if ($token->[2]->{src} =~ m=^(https?|ftp)://|^/=i) {
      print "\t" . $token->[2]->{src} . "\n";
    } else {
      print "\t" . $base . "/" . $token->[2]->{src} . "\n";
    }
  };
  print "\n";
};

아래 출력 예에서 각 단락의 첫 번째 줄은 파일 이름이고 나머지 줄은 탭이 접두사로 붙은 IMG SRC URL입니다. 탭은 주로 사람이 쉽게 읽을 수 있도록 하기 위한 것이지만 파일 이름에 개행 문자가 포함되어 있는 경우에도 유용합니다. 파일 이름에 개행 문자와 탭이 포함된 경우 여전히 문제가 발생할 수 있으므로 완벽하지 않습니다. 따라서 일반적으로 NUL 문자를 파일 이름 구분 기호로 사용하는 것이 좋습니다. 이는 파일에서 유일한 문자입니다. 경로/파일 이름에 잘못된 문자가 있습니다. )

$ ./extract-img-urls2.pl ~/public_html/index*.html
/home/cas/public_html/index.html
        /home/cas/public_html/cas.jpg
        /home/cas/public_html/valid-html401.png

/home/cas/public_html/index.old.html
        /home/cas/public_html/cas.jpg


Perl 프로그래밍, 변수 참조, 배열, 객체 등

요약: 정말 놀랍습니다.

그런데 왜 스크립트에서 $token->[1]및 와 같은 변수를 사용하는지 궁금할 것입니다 $token->[2]->{src}. 그 이유는 메서드에서 반환된 개체의 구조를 확인했는데 get_token개체 HTML::TokeParser::Simple::Token::Tag::Start의 데이터 구조가 다음과 같기 때문입니다.

[
  'S',
  'img',
   { 'src' => 'valid-html401.png',
     'width' => '88',
     'alt' => 'Valid HTML 4.01 Transitional',
     'height' => '31'
   },
   [ 'src',     
     'alt',       
     'height',
     'width'      
   ],
   '<img src="valid-html401.png" alt="Valid HTML 4.01 Transitional" height="31" width="88">'
]

이것은 요소 0으로 문자열 'S'(현재 태그가 시작 태그임을 의미합니다. <p>... "E"는 종료 태그를 의미합니다 .), 요소 1 문자열로 </p>HTML 태그 이름을 포함하는 문자 , 속성 이름(키)과 HTML 태그 값을 요소 2로 포함하는 'img'해시(연관 배열, in ), 요소 4(in )로 속성 이름을 다시 포함하는 또 다른 인덱스 배열 또는 "목록" 및 img의 실제 HTML 텍스트 src 태그를 요소 5로 사용합니다.{...}[...]

man HTML::TokeParser이에 대한 설명은 "S" 유형 토큰에 가 있다고 명시 되어 있습니다 ["S", $tag, $attr, $attrseq, $text]. 해시($attr)와 해시 키($attrseq)가 포함된 배열이 포함된 Argspec이유를 부분적으로 설명합니다 . 이는 해시가 본질적으로 순서가 없고 HTML 소스 코드의 태그에 표시된 키의 원래 순서를 man HTML::Parser기억하는 데 배열이 사용되기 때문입니다 . img이는 키 순서를 잃지 않고 해싱의 편의성을 얻는 매우 일반적인 기술입니다.

Perl 코드에서 $token->[1]두 번째 요소(perl 배열은 1이 아닌 0에서 시작)를 참조하므로 해당 요소가 img대소문자를 구분하는지 확인합니다. 그렇다면 src해시의 키를 :에 인쇄합니다.$token->[2]$token->[2]->{src}

"화살표 연산자"라고 하며 ->C나 C++에서 사용되는 방식과 마찬가지로 데이터 구조의 값을 역참조(액세스)하는 데 사용됩니다.

(lol="lists-of-lists", AKA arrays-of-arrays(다른 배열을 포함하는 배열) 및 ("Perl 데이터 구조 요리책") 정보)의 매뉴얼 페이지를 읽어perldata Perl 데이터에 대해 자세히 알아볼 수 있습니다. . 튜토리얼도 참조하세요.perllolperldscman perlrefman perlreftut

화살표 연산자는 Perl 객체 지향 프로그래밍에서 메소드(예: 서브루틴)를 호출하는 데에도 사용됩니다( 참조 man perlobj)... 위의 코드에서 새 객체가 $p = HTML::TokeParser::Simple->new(...)생성되고 메소드 가 예호출되는 .)$pHTML::TokeParser::Simple$p->get_token$pget_token()$token

다음과 같은 모듈을 사용할 수 있습니다.데이터::덤프또는데이터::덤퍼이와 같은 코드를 개발/디버깅할 때 데이터 구조와 개체를 예쁘게 인쇄합니다. 문서를 검색하는 것보다 더 빠르고 작업량이 적은 경우가 많습니다.

Data:DumperPerl에 포함된 핵심 모듈입니다. 아니요, 하지만 Debian 등에 설치 하거나 사용하기 Data::Dump쉽습니다 . 둘 다 좋지만 일반적으로 .apt-get install libdata-dump-perlcpanData::Dump

그건 그렇고, bash배열 및 연관 배열(일명 "해시")을 지원하는 bash에서는 이러한 복잡한 데이터 구조를 수행할 수 없지만 요소는 단일 문자열이나 숫자와 같은 간단한 스칼라 값만 포함할 수 있습니다. 중첩된 배열이나 해시를 포함할 수 없습니다. Bash(및 다른 bourne과 유사한 쉘)에는 몇 가지 변수 인용 기능이 있지만 이를 사용하고 있다면 더 나은 언어(거의 다른 언어)를 사용해야 합니다. bash는 데이터 처리에 적합하지 않습니다. 다른 프로그램(grep, sed, cut, perl, awk 등)의 실행을 설정하고 조정하는 데 사용되는 언어입니다.

답변2

사용sed

$ sed -n '/<img/s/.*src=\([^ ]*\).*/\1/p' file1 file2
https://www.example.com/image1.jpg
https://www.example.com/image2.jpg

답변3

이것은 XML 파서를 사용하는 솔루션입니다.

for file in *.html
do
    xmlstarlet format --html "$file" 2>/dev/null |
        xmlstarlet select --template --value-of '//img/@src' --nl
done >output.txt

HTML을 올바른 형식의 XML로 강제 변환한 다음 src각 태그에서 속성 값을 차례로 선택하는 것입니다. <img/>전체 결과 세트 작성output.txt

HTML 파일이 이미 올바른 형식의 XML인 경우 명시적 루프를 생략하고 전체 시퀀스를 단일 명령으로 줄일 수 있습니다.

xmlstarlet select --template --value-of '//img/@src' --nl *.html >output.txt

또는 덜 장황한

xmlstarlet sel -t -v '//img/@src' -n *.html >output.txt

답변4

이것은 작동합니다:

grep '^<img' *.html | grep -o 'http[^"]*' > output.txt

관련 정보