我正试图从一些网页上获取干净的文字。
我已经阅读了很多教程,最后得到了python lxml
+ beautifulsoup
+ requests
个模块。
使用lxml
进行此类任务的原因是它比美丽的汤更能清除html文件。
我最终得到了这样的测试脚本:
from bs4 import UnicodeDammit
import re
import requests
import lxml
import lxml.html
from time import sleep
urls = [
"http://mathprofi.ru/zadachi_po_kombinatorike_primery_reshenij.html",
"http://ru.onlinemschool.com/math/assistance/statistician/",
"http://mathprofi.ru/zadachi_po_kombinatorike_primery_reshenij.html",
"http://universarium.org/courses/info/332",
"http://compsciclub.ru/course/wordscombinatorics",
"http://ru.onlinemschool.com/math/assistance/statistician/",
"http://lectoriy.mipt.ru/course/Maths-Combinatorics-AMR-Lects/",
"http://www.youtube.com/watch?v=SLPrGWQBX0I"
]
def check(url):
print "That is url {}".format(url)
r = requests.get(url)
ud = UnicodeDammit(r.content, is_html=True)
content = ud.unicode_markup.encode(ud.original_encoding, "ignore")
root = lxml.html.fromstring(content)
lxml.html.etree.strip_elements(root, lxml.etree.Comment,
"script", "style")
text = lxml.html.tostring(root, method="text", encoding=unicode)
text = re.sub('\s+', ' ', text)
print "Text type is {}!".format(type(text))
print text[:200]
sleep(1)
if __name__ == '__main__':
for url in urls:
check(url)
因为html页面可能包含一些与大多数其他字符编码不同的字符,所以需要对原始编码进行非中性处理和重新编码。这样的场合进一步打破了lxml tostring
方法。
但是我的代码在所有测试中都无法正常工作。有时候(尤其是最后两个网址)会输出混乱:
...
That is url http://ru.onlinemschool.com/math/assistance/statistician/
Text type is <type 'unicode'>!
Онлайн решение задач по математике. Комбинаторика. Теория вероятности. Close Авторизация на сайте Введите логин: Введите пароль: Запомнить меня Регистрация Изучение математики онлайн.Изучайте математ
That is url http://lectoriy.mipt.ru/course/Maths-Combinatorics-AMR-Lects/
Text type is <type 'unicode'>!
ÐаÑемаÑика. ÐÑÐ½Ð¾Ð²Ñ ÐºÐ¾Ð¼Ð±Ð¸Ð½Ð°ÑоÑики и ÑеоÑии ÑиÑел / ÐидеолекÑии ФизÑеÑа: ÐекÑоÑий ÐФТР- видеолекÑии по Ñизике,
That is url http://www.youtube.com/watch?v=SLPrGWQBX0I
Text type is <type 'unicode'>!
ÐÑновнÑе ÑоÑмÑÐ»Ñ ÐºÐ¾Ð¼Ð±Ð¸Ð½Ð°ÑоÑики - bezbotvy - YouTube ÐÑопÑÑÑиÑÑ RU ÐобавиÑÑ Ð²Ð¸Ð´ÐµÐ¾ÐойÑиÐоиÑк ÐагÑÑзка... ÐÑбеÑиÑе ÑзÑк.
这个混乱与编码ISO-8859-1
有某种联系,但我无法弄清楚如何。
对于我得到的最后两个网址中的每一个:
In [319]: r = requests.get(urls[-1])
In [320]: chardet.detect(r.content)
Out[320]: {'confidence': 0.99, 'encoding': 'utf-8'}
In [321]: UnicodeDammit(r.content, is_html=True).original_encoding
Out[321]: 'utf-8'
In [322]: r = requests.get(urls[-2])
In [323]: chardet.detect(r.content)
Out[323]: {'confidence': 0.99, 'encoding': 'utf-8'}
In [324]: UnicodeDammit(r.content, is_html=True).original_encoding
Out[324]: u'utf-8'
所以我猜lxml
基于输入字符串的错误假设进行内部解码。我认为它甚至没有尝试猜测输入字符串编码。似乎在lxml的核心中发生了类似这样的事情:
In [339]: print unicode_string.encode('utf-8').decode("ISO-8859-1", "ignore")
ÑÑÑока
我如何解决我的问题并清除html标签中的所有网址? 也许我应该使用另一个python模块或以其他方式做? 请给我你的建议。
答案 0 :(得分:1)
我终于明白了。 解决方案是不使用
root = lxml.html.fromstring(content)
但配置一个显式的Parser对象,可以告诉它使用特定的编码enc
:
htmlparser = etree.HTMLParser(encoding=enc)
root = etree.HTML(content, parser=htmlparser)
另外,我发现即使UnicodeDammit
在决定页面编码时也会犯明显错误。所以我添加了另一个if
块:
if (declared_enc and enc != declared_enc):
以下是结果的片段:
from lxml import html
from lxml.html import etree
import requests
from bs4 import UnicodeDammit
import chardet
try:
self.log.debug("Try to get content from page {}".format(url))
r = requests.get(url)
except requests.exceptions.RequestException as e:
self.log.warn("Unable to get page content of the url: {url}. "
"The reason: {exc!r}".format(url=url, exc=e))
raise ParsingError(e.message)
ud = UnicodeDammit(r.content, is_html=True)
enc = ud.original_encoding.lower()
declared_enc = ud.declared_html_encoding
if declared_enc:
declared_enc = declared_enc.lower()
# possible misregocnition of an encoding
if (declared_enc and enc != declared_enc):
detect_dict = chardet.detect(r.content)
det_conf = detect_dict["confidence"]
det_enc = detect_dict["encoding"].lower()
if enc == det_enc and det_conf < THRESHOLD_OF_CHARDETECT:
enc = declared_enc
# if page contains any characters that differ from the main
# encodin we will ignore them
content = r.content.decode(enc, "ignore").encode(enc)
htmlparser = etree.HTMLParser(encoding=enc)
root = etree.HTML(content, parser=htmlparser)
etree.strip_elements(root, html.etree.Comment, "script", "style")
text = html.tostring(root, method="text", encoding=unicode)