Python线程池执行器线程未完成
我有一个使用Python 3.8.2使用concurrent.futures.ThreadPoolExecutor对页面进行爬网的脚本。从本质上讲,它会在一个页面上抓取链接,使用sqlalchemy将它们存储在sqlite中,然后再移动到下一个页面 我有一个问题,但脚本永远不会完成。我已经确保使用两个print语句完成所有进程,但是脚本只是挂起,从未完成。关于如何处理并发性和sqlite会话,我是否遗漏了什么Python线程池执行器线程未完成,python,multithreading,concurrency,sqlalchemy,Python,Multithreading,Concurrency,Sqlalchemy,我有一个使用Python 3.8.2使用concurrent.futures.ThreadPoolExecutor对页面进行爬网的脚本。从本质上讲,它会在一个页面上抓取链接,使用sqlalchemy将它们存储在sqlite中,然后再移动到下一个页面 我有一个问题,但脚本永远不会完成。我已经确保使用两个print语句完成所有进程,但是脚本只是挂起,从未完成。关于如何处理并发性和sqlite会话,我是否遗漏了什么 from sqlalchemy import create_engine, Colum
from sqlalchemy import create_engine, Column, String
from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy.ext.declarative import declarative_base
def crawl(link):
print('Starting: {}'.format(link))
session = Session()
html = requests.get(url, timeout=10)
soup = BeautifulSoup(html.text, 'lxml')
links = [entry.get('href') for entry in soup.find_all('a', clazz)]
for link in links:
data = {
'type': self.type,
'status': self.status,
'url': link
}
if not session.query(exists().where(Table.url == link)).scalar():
d = DataEntry(**data)
session.add(d)
session.commit()
print('Finished: {}'.format(link))
def main():
links = ['www.link1.com', 'www.link2', ....]
with futures.ThreadPoolExecutor(max_workers=4) as executor:
the_futures = [executor.submit(crawl_for_listings, task) for task in tasks]
for future in the_futures:
try:
result = future.result()
except Exception as e:
print('Thread threw exception:', e)
if __name__ == "__main__":
engine = create_engine("sqlite:///database.sql")
Base = declarative_base()
class Links(Base):
__tablename__ = 'links'
url = Column(String, primary_key=True)
type = Column(String)
status = Column(String)
Base.metadata.create_all(engine)
session_factory = sessionmaker(bind=engine)
Session = scoped_session(session_factory)
main()
Session.remove()
您对
submit
的呼叫应该是:
future = executor.submit(crawl, link)
不是:
在第一种情况下,您将向submit
传递对函数及其参数的引用。在第二种情况下,首先调用函数,然后将该调用的返回值传递给submit
,返回值显示为None
。然后,您应该保存返回的future
对象,并且可以在线程出现时测试线程的完成情况,因此:
with futures.ThreadPoolExecutor(max_workers=4) as executor:
the_futures = []
for link in links:
future = executor.submit(crawl, link)
the_futures.append(future)
for future in futures.as_completed(the_futures):
#print(future.result()) # result is None in this case
pass
或更“Pythonically”:
还请注意,我正在使用上下文管理器创建变量executor
,以便在块终止时完成任何必要的清理(调用shutdown
,这将等待所有未来完成,但我在退出块之前显式地等待未来完成)
如果您注意按创建顺序返回结果(在这种情况下,您不会这样做,因为返回的结果总是None
):
但是,当您想要获得所有结果时,上面的executor.map
函数就不那么方便了,而且一个或多个线程可能会引发异常,因为您将无法从引发异常的第一个线程之外的线程检索结果(即使假设您使用try/except
块来获取结果)。当您调用的函数使用的不是一个参数时,使用它也会更加复杂。因此,在这种情况下,最好使用futures:
with futures.ThreadPoolExecutor(max_workers=4) as executor:
the_futures = [executor.submit(crawl, link) for link in links]
for future in the_futures:
try:
result = future.result() # could throw an exception if the thread threw an exception
print(result)
except Exception as e:
print('Thread threw exception:', e)
有了以上所有内容,我仍然不确定为什么您的程序没有终止。有一件事是肯定的:您不是多线程。我建议检查这个问题:
任务现在是如何定义的?也许您应该显示更多的代码。这只是我写错了,应该是链接。这只是一个我想从中提取列出的URL的字符串。感谢您的输入。您是对的,这对我来说是一个新领域,所以我可能仍然有错误。尽管如此,我仍然有同样的问题,我想知道这是否与sqlalchemy会话有关。我曾评论说我认为会话.close()
在爬网中
可能是问题所在。但我已经找到了一些例子,说明应该在哪里进行爬网。根据我在web上的发现(不是我个人的经验),代码看起来是正确的。你的线程中有没有出现异常而失败的?但是想到异常,我总的来说会想为什么会这样(这不一定能解决您的问题)如果线程可能引发异常,那么您最好不要使用executor.map
函数,而应该使用executor.submit
,它返回一个future
。我已经更新了我的答案来解释这一点。现在我已经让它工作了。似乎会话.close()这就是问题所在。至少我的代码运行了很多次,我尝试了很多次迭代来确保,数据被添加到了我的数据库中。考虑到这么多人建议使用session.close()…我查看了一些示例,他们似乎使用了session.close()
,从您执行session=session()时起,我就开始质疑这一点
,我认为每个线程都获得相同的会话对象。请参阅:。方式1代码使用每个线程的会话和函数thread\u worker
中的上下文管理器,以确保在函数结束之前,它将首先在会话上调用close
。但使用作用域\u会话的方式2代码则没有调用close
。
with futures.ThreadPoolExecutor(max_workers=4) as executor:
the_futures = [executor.submit(crawl, link) for link in links]
for future in futures.as_completed(the_futures):
pass
with futures.ThreadPoolExecutor(max_workers=4) as executor:
for result in executor.map(crawl, links):
#print(result) # None in this case
pass
with futures.ThreadPoolExecutor(max_workers=4) as executor:
the_futures = [executor.submit(crawl, link) for link in links]
for future in the_futures:
try:
result = future.result() # could throw an exception if the thread threw an exception
print(result)
except Exception as e:
print('Thread threw exception:', e)