我在文本文件中有成千上万个URL,现在我想从产品链接中提取标题和价格。我试图实现线程以使其更快地执行,但是似乎它无法正常工作,从而产生重复的数据并且执行脚本的时间过长。在不使用线程的情况下,脚本可以按预期工作。
这是我的代码:
import requests
from bs4 import BeautifulSoup
import csv
import lxml
import threading
def runner(fname):
global lck
lck.acquire()
with open(fname, 'r') as f:
for line in f:
r = requests.get(line)
soup = BeautifulSoup(r.content, 'lxml')
try:
title = soup.find('h1', id='itemTitle').text.trim().encode('utf-8')
price = soup.find('span', itemprop='price').text.trim().encode('utf-8')
except:
price = "No price"
with open("Data.csv", 'a', newline='',) as file:
writer = csv.writer(file)
writer.writerow([title, price])
lck.release()
lck = threading.Lock()
fname = "ProductLinks.txt"
threads = []
for i in range(0, 3):
t = threading.Thread(target = runner, args = (fname, ))
threads.append(t)
t.start()
for t in threads:
t.join()
有人可以指导我如何正确执行操作,以便它可以并行提取和保存数据
答案 0 :(得分:2)
之所以会产生重复的结果,是因为在创建线程时,您会多次调用同一函数。
t = threading.Thread(target = runner, args = (fname, ))
执行上述行时,参数始终为fname
,据我所知始终为"ProductLinks.txt"
。因此,您的程序将进入runner
,在那里我看到您遍历了文本的所有行。
我怀疑您要“并行化”的内容正是在文本行上循环吗?然后,您将需要编写一个函数parse_line
并将其传递到线程环境中。
我还建议您将值存储在字典中,最后导出到csv
,因为不确定open
环境是否是线程安全的。
def parse_line(line, result_dict):
r = requests.get(line)
soup = BeautifulSoup(r.content, 'lxml')
try:
title = soup.find('h1', id='itemTitle').text.trim().encode('utf-8')
price = soup.find('span', itemprop='price').text.trim().encode('utf-8')
result_dict[title] = price
except:
result_dict['No title'] = "No price"
现在,假设您有一个列表,其中文件中的所有行均为字符串。您可以执行以下操作
file_lines = []
with open(fname, 'r') as f:
for line in f:
file_lines.append(line)
然后您可以在文件中所有行的列表中使用Threading
调用此函数
my_dict = {}
for input_line in file_lines:
t = threading.Thread(target = parse_line, args = (input_line, my_dict))
threads.append(t)
t.start()
最后,您可以使用熊猫将字典导出到csv
import pandas as pd
pd.DataFrame(my_dict).to_csv("Data.csv")