본문 바로가기

Python

[Python/NLP] 위키피디아 덤프 데이터에서 하이퍼링크(anchor text) 추출하기

 

 

 

wiki의 dump 데이터에서 plain text를 추출하기 위한 도구로써 wiki extractor를 패키지가 존재합니다.

해당 방법에 대한 포스트는 다음 글에서 자세히 설명하고, 이번 글에서는 html태그를 포함한 plain text가 준비되었다는 전제 하에 앵커 텍스트를 추출하는 방법에 대해 소개하고자 합니다.

 

 

 

Anchor text (앵커 텍스트)

 

anchor text는 HTML 하이퍼 링크에서 볼 수 있고 클릭 가능한 텍스트입니다. "anchor"라는 용어는 현재 a 요소 또는 라고하는 HTML 사양의 이전 버전에서 사용되었습니다.

 

 

위키 페이지 내에서는 파란색으로 나타나는 텍스트가 anchor text 이며, 해당 텍스트는 위키 내에서 자신만의 고유한 페이지가 존재 합니다.

위의 섹션을 개발자 도구를 띄워 확인해보면 다음과 같은 형식으로 데이터를 확인할 수 있습니다.

 

<a href=[위키 내의 앵커 텍스트 페이지 링크] title=[앵커 텍스트의 타이틀]>[앵커 텍스트]</a>

 

 

 

이때, 주의할 점은 anchor text와 해당 텍스트의 위키 내 페이지 타이틀이 "항상 같지는 않을 수 있다" 는 것입니다.

아래 예시에서 anchor text는 미국에 사는 흑인 이지만, 해당 텍스트를 클릭했을 때 나오는 위키 페이지의 타이틀은 아프리카계 미국인 이 됩니다.

 

 

 

 

이번 글에서는 하나의 엔티티에 대하여 해당 위키 페이지 내의 anchor text (미국에 사는 흑인)와 anchor title (아프리카계 미국인) 모두 추출한 과정을 공유하고자 합니다.

 

 

 


 

Anchor가 보존된 위키 내의 전문 추출

 

WikiExtractor의 사용법에 따르면 전달 인자 중 -l 이 있습니다. 이 인자는 link를 보존하여 plain한 text를 추출할 수 있도록 합니다.

usage: wikiextractor [-h] [-o OUTPUT] [-b n[KMG]] [-c] [--json] [--html] [-l] [-ns ns1,ns2] [--templates TEMPLATES] [--no-templates] [--html-safe HTML_SAFE] [--processes PROCESSES] [-q] [--debug] [-a] [-v] input

 

링크가 보존되기 전과 후를 비교해 보겠습니다

 

1. 적용 전

제임스 얼 카터 주니어(James Earl Carter, Jr., 1924년 10월 1일 ~ )는 민주당 출신 미국 39대 대통령 ...

 

2. 적용 후

제임스 얼 카터 주니어(James Earl Carter, Jr., <a href="1924년">1924년</a> <a href="10월 1일">10월 1일</a> ~ )는 <a href="민주당 (미국)">민주당</a> 출신 <a href="미국">미국</a> 39대 대통령...

 

 

결과를 보면 다음과 같은 구조를 띄는 것을 확인할 수 있습니다.

<a href="[anchor title]">[anchor text]</a>

 

 

이제 정규표현식을 이용하면 두개의 쌍을 추출해 낼 수 있을 것 같습니다!

 

 

전처리

 

그 전에, 필요한 전처리 과정이 있습니다.

사실 wiki extractor로 텍스트를 추출한 후에 결과를 확인해보면 한글이 유니코드로 표현 되어있는 것을 확인할 수 있습니다.

James Earl Carter Jr.\n\uc81c\uc784\uc2a4 \uc5bc \uce74\ud130 \uc8fc\ub2c8\uc5b4(James Earl Carter, Jr., <a href="1924%EB%85%84">1924\ub144 <a href="10%EC%9B%94%201%EC%9D%BC">10\uc6d4 1\uc77c ~ )\ub294 <a href="%EB%AF%BC%EC%A3%BC%EB%8B%B9%20%28%EB%AF%B8%EA%B5%AD%29">\ubbfc\uc8fc\ub2f9 \ucd9c\uc2e0 <a href="%EB%AF%B8%EA%B5%AD">\ubbf8\uad6d 39\ub300 \ub300\ud1b5\ub839

 

 

만약 링크를 보존하지 않은 상태라면, 해당 텍스트를 print 했을 때 아래와 같이 잘 나오지만,

제임스 얼 카터 주니어(영어: James Earl Carter, Jr., 1924년 10월 1일 ~ )는 민주당 출신 
미국 39대 대통령

 

 

링크를 보존한 상태라면 print결과가 다음과 같게 됩니다.

제임스 얼 카터 주니어(James Earl Carter, Jr., &lt;a href="1924%EB%85%84"&gt;1924년&lt;/a&gt; &lt;a 
href="10%EC%9B%94%201%EC%9D%BC"&gt;10월 1일&lt;/a&gt; ~ )는 &lt;a 
href="%EB%AF%BC%EC%A3%BC%EB%8B%B9%20%28%EB%AF%B8%EA%B5%AD%29"&gt;민주당&lt;/a&gt; 출신 &lt;a 
href="%EB%AF%B8%EA%B5%AD"&gt;미국&lt;/a&gt; 39대 대통령

 

 

1. html escape 코드 변환

 

우리가 원하는 <a href></a> 가 아닌 &lt; &gt; 로 html의 escape 문자의 코드 그대로 나타납니다. 따라서 이를 변환하기 위해서 전처리를 추가해줍니다.

 

from xml.sax import saxutils as su
text = "James Earl Carter Jr.\n\uc81c\uc784\uc2a4 \uc5bc \uce74\ud130 \uc8fc\ub2c8\uc5b4(James Earl Carter, Jr., <a href=\"1924%EB%85%84\">1924\ub144</a> <a href=\"10%EC%9B%94%201%EC%9D%BC\">10\uc6d4 1\uc77c</a> ~ )\ub294 <a href=\"%EB%AF%BC%EC%A3%BC%EB%8B%B9%20%28%EB%AF%B8%EA%B5%AD%29\">\ubbfc\uc8fc\ub2f9</a> \ucd9c\uc2e0 <a href=\"%EB%AF%B8%EA%B5%AD\">\ubbf8\uad6d</a> 39\ub300 \ub300\ud1b5\ub839"
prep_text1 = su.unescape(text)
print(prep_text1)

> 'James Earl Carter Jr.\n제임스 얼 카터 주니어(James Earl Carter, Jr., <a href="1924%EB%85%84">1924년</a> <a href="10%EC%9B%94%201%EC%9D%BC">10월 1일</a> ~ )는 <a href="%EB%AF%BC%EC%A3%BC%EB%8B%B9%20%28%EB%AF%B8%EA%B5%AD%29">민주당</a> 출신 <a href="%EB%AF%B8%EA%B5%AD">미국</a> 39대 대통령'

 

 

이제 결과가 <a href></a> 형태로 잘 나왔습니다! 그런데 여전히 문제가 있습니다. 이라는 한글이 퍼센트 인코딩 되어있네요.

 

<a href="1924%EB%85%84">1924년</a>

 

 

2. URL 한글 인코딩 변환

 

퍼센트 인코딩이란❓

 

퍼센트 인코딩(percent-encoding)은 URL에 문자를 표현하는 문자 인코딩 방법으로 영문자, 숫자, 몇몇 기호만을 사용하여 문자를 나타냅니다. 이 외에 한글, 한자, 특수문자 등은 사용할 수 없습니다. 때문에 파이썬에서 한글 포함된 URL 주소에 요청을 보내면 오류가 발생하게 됩니다. 따라서 한글 텍스트를 퍼센트 인코딩하여 url이 작성됩니다.

 

import urllib
urllib.parse.quote('행주')

> '%ED%96%89%EC%A3%BC'

 

 

따라서 퍼센트 인코딩된 텍스트를 다시 한글로 변환해주기 위해 다음과 같은 전처리를 추가합니다.

import urllib
prep_text2 = urllib.parse.unquote(prep_text1)
print(prep_text2)

> '제임스 얼 카터 주니어(James Earl Carter, Jr., <a href="1924년">1924년</a> <a href="10월 1일">10월 1일</a> ~ )는 <a href="민주당 (미국)">민주당</a> 출신 <a href="미국">미국</a> 39대 대통령'

 

이제 원하는 텍스트가 만들어졌고, 앵커 텍스트를 추출할 준비가 된 것 같습니다. ㅎㅎ

 

전처리 코드

두 과정을 요약하면 다음과 같습니다.

import urllib
from xml.sax import saxutils as su
urllib.parse.unquote(su.unescape(text))

 

 

정규표현식을 이용하여 앵커 텍스트 추출

 

이제 위에서 만들어진 text를 가지고 정규표현식 중 주어진 패턴에 해당하는 모든 결과를 반환하는 findall 을 이용하여 anchor title과 text를 추출해보겠습니다.

text = '제임스 얼 카터 주니어(James Earl Carter, Jr., <a href="1924년">1924년</a> <a href="10월 1일">10월 1일</a> ~ )는 <a href="민주당 (미국)">민주당</a> 출신 <a href="미국">미국</a> 39대 대통령'
pattern = "<a href=\"(.*?)\">(.*?)<\/a>"
match_list = re.findall(pattern, text)
print(match_list)

> [('1924년', '1924년'), ('10월 1일', '10월 1일'), ('민주당 (미국)', '민주당'), ('미국', '미국')]

 

 

여기서 정규표현식을 테스트하는 유용한 사이트를 소개하고자 합니다. regex101은 텍스트에 대해 실시간으로 정규표현식을 적용시켜 볼 수 있고, 해당 매치 결과에 대한 설명도 있기 때문에 어떤 패턴에 대한 매치 결과인지 확인하고 원리를 파악하는 데 도움이 많이 되었습니다.

 

 

🌐 regex101

 

 

매칭된 결과는 anchor title과 text의 튜플의 리스트인데, 만약 여기서 title은 title끼리, text는 text끼리 각각의 리스트를 생성하고 싶다면 zip함수를 이용하여 분리시킬 수 있습니다.

 

unzipped_object = zip(*match_list)
unzipped_list = list(unzipped_object)
anchor_titles = list(unzipped_list[0])
anchor_texts = list(unzipped_list[1])

print(anchor_titles)
print(anchor_texts)

> ['1924년', '10월 1일', '민주당 (미국)', '미국']
> ['1924년', '10월 1일', '민주당', '미국']

 

 

Reference.

html의 escape문자 변환하는 법

python url 문자열 변환 url 한글 인코딩 디코딩