Python子线程中创建BeautifulSoup对象出现encoding error,主线程正常,如何解决?

Python 子线程里创建 BeautifulSoup 对象会在终端打印encoding error : input conversion failed due to input error, 主线程里面一切正常。

感觉应该是 lxml 的问题,但是没找到怎么解决,求解惑

就几个特定的网页会出现这个问题,比如 http://zhuanlan.sina.com.cn/

下面这个代码可以用来复现

import requests
from bs4 import BeautifulSoup
from threading import Thread
def test():
    r = requests.get('http://zhuanlan.sina.com.cn/')
    soup = BeautifulSoup(r.content,'lxml')

print(‘在主线程中执行 test’) test()

print(‘在子线程中执行 test’) t = Thread(target=test) t.start() t.join()

输出如下

在主线程中执行 test
在子线程中执行 test
encoding error : input conversion failed due to input error, bytes 0x95 0x50 0x22 0x20
encoding error : input conversion failed due to input error, bytes 0x95 0x50 0x22 0x20
encoding error : input conversion failed due to input error, bytes 0x95 0x50 0x22 0x20

Python子线程中创建BeautifulSoup对象出现encoding error,主线程正常,如何解决?

11 回复

把你的解析那段替换成以下代码,不是线程问题,是编码问题。
soup = BeautifulSoup(r.content.decode(‘ISO-8859-1’),‘lxml’)


这个问题通常是因为子线程的环境与主线程不同,特别是标准输出/错误流或本地化设置导致的。BeautifulSoup在解析时如果遇到某些字符编码问题,可能会尝试使用sys.stdout.encoding等环境信息,而子线程中这些可能为None或不同。

最直接的解决方案是在子线程中显式指定文件编码或使用字节串创建BeautifulSoup对象。以下是两种方法:

方法1:使用字节串并指定编码

from bs4 import BeautifulSoup
import threading

def parse_in_thread(html_bytes):
    # 使用字节串并明确指定编码
    soup = BeautifulSoup(html_bytes, 'html.parser', from_encoding='utf-8')
    # 或者让BeautifulSoup自动检测
    # soup = BeautifulSoup(html_bytes, 'html.parser')
    # 处理soup对象...

# 在线程中调用时传递字节串
html_bytes = response.content  # 如果是requests获取的
thread = threading.Thread(target=parse_in_thread, args=(html_bytes,))

方法2:设置线程环境变量

import threading
import sys
import io

def thread_parse(html_str):
    # 临时设置标准输出的编码
    original_stdout = sys.stdout
    sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
    
    try:
        soup = BeautifulSoup(html_str, 'html.parser')
        # 处理...
    finally:
        sys.stdout = original_stdout

更稳健的方案:使用线程池并统一编码处理

from concurrent.futures import ThreadPoolExecutor
import requests
from bs4 import BeautifulSoup

def parse_html(url):
    resp = requests.get(url)
    # 始终使用.content(字节串)而不是.text(解码后的字符串)
    soup = BeautifulSoup(resp.content, 'html.parser')
    
    # 如果需要,可以显式设置soup的编码
    if soup.original_encoding:
        soup.original_encoding = 'utf-8'
    
    return soup.find('title').text if soup.find('title') else 'No title'

# 使用线程池
with ThreadPoolExecutor(max_workers=5) as executor:
    urls = ['http://example.com', 'http://example.org']
    results = list(executor.map(parse_html, urls))

关键点:在线程中处理HTML时,始终使用字节流(.content)而非解码后的字符串,并让BeautifulSoup处理编码检测。

建议:在线程中使用字节数据并明确编码。

谢谢啦,不过应该不是编码的问题,试了下子线程里面还是会打印 encoding error, 主线程还是正常的
![截图]( )

试下 r.content.decode(‘gb2312’, ‘ignore’).encode(‘gb2312’)

好吧,给你的那段代码,我已经在本机测试通过的,不知道你那边是什么个情况。

这样就可以了。。 但是之前为什么主线程里面跑就正常,子线程里就不行呢, 好迷啊= =

import requests
from bs4 import BeautifulSoup
from threading import Thread


def test():
r = requests.get(‘http://zhuanlan.sina.com.cn/’)
print(r.encoding)
soup = BeautifulSoup(r.content.decode(‘utf-8’, ‘ignore’), ‘lxml’)


print(‘在主线程中执行 test’)
test()

print(‘在子线程中执行 test’)
t = Thread(target=test)
t.start()
t.join()

注意看加的那一行的输出呦~

加的那一行的输出是 ISO-8859-1,应该是 response header 里没有编码信息,然后 requests 按照标准默认用错误的 ISO-8859-1 解码了,但是还是没搞懂他这个多线程是怎么回事。。

看起来是 lxml 的问题了 我试过用 html.parser 和 html5lib 都没有问题诶

soup = BeautifulSoup(r.content, ‘lxml’, from_encoding=r.encoding)

囧,试了下不行,soup 的编码还是有问题 orz

回到顶部