나는 grep이 많은 답변에 나타나는 것을 보았지만 실제로 그것에 대해 생각해 본 적이 없습니다.
이제 인터넷에서 로컬로 저장된 페이지의 HTML 태그 내부 콘텐츠를 가져오려고 하는데 멈췄습니다. grep을 사용하여 원하는 출력을 식별할 수 있지만 이를 사용 가능하도록 구분하는 것은 불가능합니다.
이것은 내 test.sh 파일 내용입니다.
a=$(awk '/<div class="power-bar-text">/,/<\/div>/' 'Acid Fast.html')
b=$(echo $a | grep -PzTo [0-9\.]+)
echo $a
echo $b
결과는 다음과 같은 터미널 출력입니다.
test.sh: line 4: warning: command substitution: ignored null byte in input
<div class="power-bar-text"> 9 </div> <div class="power-bar-text"> 8 </div> <div class="power-bar-text"> 11.25 </div> <div class="power-bar-text"> 10 </div> <div class="power-bar-text"> 6 </div> <div class="power-bar-text"> 5 </div> <div class="power-bar-text"> 2 (1s) </div> <div class="power-bar-text"> 3 </div> <div class="power-bar-text"> 2.50 </div>
9811.2510652132.50
다음은 사람이 좀 더 읽기 쉬운 이전 반복입니다.
$ awk '/<div class="power-bar-text">/,/<\/div>/' 'Acid Fast.html' | grep -Pzn -C1 [0-9\.]+ -
1: <div class="power-bar-text">
9
</div>
<div class="power-bar-text">
8
</div>
<div class="power-bar-text">
11.25
</div>
<div class="power-bar-text">
10
</div>
<div class="power-bar-text">
6
</div>
<div class="power-bar-text">
5
</div>
<div class="power-bar-text">
2 (1s)
</div>
<div class="power-bar-text">
3
</div>
<div class="power-bar-text">
2.50
</div>
위의 코드 상자에서 색상을 설정하는 방법을 모르지만 터미널은 기본 일치 글꼴 색상인 빨간색을 사용하여 각 숫자와 마침표를 인코딩합니다.
(클래스 이름이 마침표와 일치하기 때문에 "power.bar.text"인 경우 작동하지 않을 수 있습니다. 마침표가 숫자인지 확인하는 데 도움이 되나요? 정규 표현식에 적용될 수 있을 것 같습니다 [0-9]+\.?[0-9]*
.)
그러나 Bash의 코드를 사용하여 첫 번째 코드 블록으로 돌아가면, 그것이 제공하는 최종 출력은 9811.2510652132.50
. 하지만 나는 다음과 같은 것을 원합니다9,8,11.25,10,6,5,2,1,3,2.50
grep 코드를 작성하면 -d,
출력에서 명령에 구분 기호를 설정하도록 선택할 수 있습니다. 불행하게도 제가 시도했을 때 그 아이디어는 효과가 없었습니다.
내가 가지고 있는 끔찍한 아이디어 중 하나는 -m 매개변수 출력을 사용하여 이를 반복적으로 처리하고 허용되는 일치 항목 수를 늘린 다음 각 출력 사이에서 새로운 것을 찾는 것입니다. 다시 말하지만, 이것은 끔찍한 것 같습니다. (예를 들어, -m1은 9를 얻고, -m2는 98을 얻고, -m3은 9811.25를 얻을 것으로 예상했으며, m1의 출력에서 m2의 출력을 "빼서" 8을 얻습니다. m3, 우리는 11.25를 얻습니다.)
실제로 방금 시도했지만 awk가 한 줄로 만든 것 같아서 작동하지 않습니다. 따라서 일치 항목 수에 관계없이 전체 문자열이 9811.2510652132.50
첫 번째이자 유일한 일치 항목이므로 전체 문자열을 출력합니다.
확실히 더 좋은 방법이 있을까요?
답변1
댓글에서 언급했듯이, grep
(구조화되지 않은 텍스트 문서에서 줄을 추출하는 유틸리티)은 일반적으로 HTML이나 구조화된 문서를 구문 분석하는 데 사용하려는 도구가 아닙니다. 이상적으로는 구조화된 쿼리를 문서에 적용하고 데이터를 추출, 수정 또는 처리할 수 있는 도구를 사용하는 것이 좋습니다. XML 문서의 경우 이러한 명령줄 도구 중 하나는 xmlstarlet
이를 사용하여 적용할 수 있다는 것입니다.XPath 쿼리XML 문서로.
div
HTML 문서가 올바른 XHTML이라고 가정하면 class
값이 속성인 노드의 내용을 추출 power-bar-text
하고 양쪽의 공백을 제거할 수 있습니다.
xmlstarlet select --template \
--match '//div[@class="power-bar-text"]' \
--value-of 'normalize-space()' -nl file.xml
이는 먼저 div
우리가 관심 있는 노드를 일치시킨 다음 normalize-space()
이러한 일치하는 노드에 적용된 함수의 결과를 추출합니다. 마지막으로 -nl
각 출력을 개행 문자로 구분합니다.
또는 짧은 옵션을 사용하세요.
xmlstarlet sel -t \
-m '//div[@class="power-bar-text"]' \
-v 'normalize-space()' -n file.xml
귀하가 표시한 문서의 일부를 고려하면 다음과 같이 출력될 수 있습니다.
9
8
11.25
10
6
5
2 (1s)
3
2.50
이 작업은 쉼표로 구분된 한 줄에 전달하여 수행할 수 있습니다.
paste -d , -s -
...이와 같이:
$ xmlstarlet sel -t -m '//div[@class="power-bar-text"]' -v 'normalize-space()' -n file.xml | paste -d , -s -
9,8,11.25,10,6,5,2 (1s),3,2.50
명령의 각 출력 줄에 있는 첫 번째 공백 이전에만 작업이 수행되도록 하려면 몇 가지 추가 처리를 추가하십시오 xmlstarlet
.
$ xmlstarlet sel -t -m '//div[@class="power-bar-text"]' -v 'normalize-space()' -n file.xml | sed 's/ .*//' | paste -d , -s -
9,8,11.25,10,6,5,2,3,2.50
파일이 XHTML이 아닌 경우 다음을 사용하여 사용할 수 있는 파일로 변환할 수 있습니다.
xmlstarlet format --recover --html file.html >file.xml
답변2
쿠살라난다에 추가답변, 보다 일반적인 HTML이 있는 경우 XML로 변환하는 대신 BeautifulSoup을 사용하는 것이 좋습니다(다른 XML 파서 자체를 사용하지 않는다는 의미는 아니며 구문 분석을 처리하는 방식이 사용 사례에 더 우아할 수 있습니다). .
스크립트를 작성하게 됩니다. 그러나 bash
스크립트는 아니지만 Python 스크립트(처음부터 직접 작성했으며 피상적으로 테스트함)를 작성합니다.
#! /usr/bin/env python3
from bs4 import BeautifulSoup
import sys
if not len(sys.argv) == 2:
print(
f"expected one argument, got {len(sys.argv) - 1}:\n {' '.join(sys.argv)}",
file=sys.stderr,
)
sys.exit(-1)
with open(sys.argv[1]) as inputfile:
soup = BeautifulSoup(inputfile)
hits = soup.find_all("div", class_="power-bar-text")
for hit in hits:
content = hit.contents[0].strip()
print(f"found value {content}")
예를 들어 파일에 저장하고 myparser.py
실행 가능하게 만들고( chmod 755 myparser.py
) HTML 파일 이름을 인수( )로 사용하여 실행합니다 /path/to/myparser.py /path/to/input.html
.
아름답고 설명이 필요한 코드는 이쯤으로 하겠습니다. 이 작업을 셸에서 수행해야 한다고 생각되면 한 줄로 압축할 수 있습니다. (이렇게 하지 말 것을 권합니다. 위의 완전한 읽기 가능하고 합리적인 오류 생성 Python 코드를 bash 스크립트의 여러 줄 문자열/HEREDOC에 포함할 수 있습니다):
infile="foo.html"
python3 -c "from bs4 import BeautifulSoup as BS;soup=BS(open('${infile}'));print('\n'.join(tag.contents[0].strip() for tag in soup.find_all('div', class_='power-bar-text')))"