Разобрать данные с помощью beatifulsoup, используя Threads - PullRequest
0 голосов
/ 03 июля 2018

У меня тысячи 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()

Может кто-нибудь подсказать мне, как правильно это сделать, чтобы он мог извлекать и сохранять данные параллельно

1 Ответ

0 голосов
/ 03 июля 2018

Это приводит к дублированию результатов, потому что при создании потоков вы вызываете одну и ту же функцию три раза.

t = threading.Thread(target = runner, args = (fname, ))

Когда вы выполняете вышеприведенную строку, аргумент всегда остается fname, насколько я понимаю, это всегда "ProductLinks.txt". Поэтому ваша программа перейдет в runner, и я вижу, что вы перебираете все строки текста.

Я подозреваю, что то, что вы хотите "распараллелить", это как раз циклическое перемещение по строкам текста? Затем вам нужно написать функцию parse_line и передать ее в среду потоков.

Я бы также посоветовал вам сохранить значения в формате dict и в конце экспортировать в 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")
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...