最近開發的爬蟲調度系統是由Flask框架提供接口,在Flask中啓動Scrapy項目,開發期間遇到了幾個問題,網上找找,自己也琢磨了好久,終於順利解決。問題如下:
一、Scrapy、crawl指令找不到
問題描述:
先看一下我的項目結構,如下:
hydra是Flask項目目錄,medical_illness下是Scrapy項目,handler_scrpy是接口文件。
現在要做的就是接口文件收到指令,然後啓動scrapy項目,在scrapy項目下的main_illess.py文件是啓動spider的,我在接口文件引入了這個文件,然後去運行它,就會報如上錯誤
Scrapy 1.7.1 - no active project
Unknown command: crawl Use "scrapy" to see
解決思路:
這是因爲,在handler_scrpy中啓動main_illess.py時當前工作目錄是在Flask項目下:
D:\文檔\個人\項目\hydra
並不在scrapy項目目錄下,所以報瞭如上錯誤。在main_illess.py中做如下修改:
import os
from scrapy import cmdline
from filter.filter_change_path import set_new_path
from .disease import spiders
def start_crawl(spider_name):
b = 'scrapy crawl '
c = b + spider_name
# 獲取spiders文件所在的目錄,並將工作目錄切換到spider所在目錄下
set_new_path(os.path.dirname(spiders.__file__))
cmdline.execute(c.split())
切換後的工作空間:
D:\文檔\個人\項目\hydra\spiders\spider_script\medical_illness\disease\disease\spiders
重點是註釋部分,獲得導入的spidres所在目錄(即scrapy所在目錄),然後見工作空間切換到scrapy目錄,然後最後一句執行爬蟲,啓動完爬蟲以後需要將工作目錄再切換回去(爲什麼要用到進程,在第二個問題中會講到):
# 啓動scrapy項目文件
def start_crawler_threads(topic_name, rules, start_url):
# 通過start_url得到spider名字
spider_name = get_spider(start_url)
# 獲得當前工作目錄(根目錄)
old_path = os.getcwd()
# 在進程中啓動爬蟲
crawl_threads = Process(target=main_illness.start_crawl, args=(spider_name ,))
crawl_threads.start()
# 啓動完線程以後交給工作目錄還原
set_old_path(old_path)
set_old_path()、set_new_path()方法所在文件定義如下:
import os
"""
更改與還原工作目錄
"""
def set_new_path(path):
os.chdir(path)
def set_old_path(path):
os.chdir(path)
到此就解決找不到scrapy和crawl的問題了。
二、ValueError: signal only works in main thread
ERROR:tornado.application:Exception in callback (<zmq.sugar.socket.Socket object at 0x7f44c4d698d0>, <function wrap.<locals>.null_wrapper at 0x7f44c4d02378>)
Traceback (most recent call last):
File "/mnt/home2/zxm/anaconda3/lib/python3.6/site-packages/tornado/ioloop.py", line 888, in start
handler_func(fd_obj, events)
File "/mnt/home2/zxm/anaconda3/lib/python3.6/site-packages/tornado/stack_context.py", line 277, in null_wrapper
return fn(*args, **kwargs)
File "/mnt/home2/zxm/anaconda3/lib/python3.6/site-packages/zmq/eventloop/zmqstream.py", line 450, in _handle_events
self._handle_recv()
File "/mnt/home2/zxm/anaconda3/lib/python3.6/site-packages/zmq/eventloop/zmqstream.py", line 480, in _handle_recv
self._run_callback(callback, msg)
File "/mnt/home2/zxm/anaconda3/lib/python3.6/site-packages/zmq/eventloop/zmqstream.py", line 432, in _run_callback
callback(*args, **kwargs)
File "/mnt/home2/zxm/anaconda3/lib/python3.6/site-packages/tornado/stack_context.py", line 277, in null_wrapper
return fn(*args, **kwargs)
File "/mnt/home2/zxm/anaconda3/lib/python3.6/site-packages/ipykernel/kernelbase.py", line 283, in dispatcher
return self.dispatch_shell(stream, msg)
File "/mnt/home2/zxm/anaconda3/lib/python3.6/site-packages/ipykernel/kernelbase.py", line 233, in dispatch_shell
self.pre_handler_hook()
File "/mnt/home2/zxm/anaconda3/lib/python3.6/site-packages/ipykernel/kernelbase.py", line 248, in pre_handler_hook
self.saved_sigint_handler = signal(SIGINT, default_int_handler)
File "/mnt/home2/zxm/anaconda3/lib/python3.6/signal.py", line 47, in signal
handler = _signal.signal(_enum_to_int(signalnum), _enum_to_int(handler))
ValueError: signal only works in main thread
問題描述:
在我成功解決工作空間的問題之後這個問題就緊接着來了,導致以上錯誤的幾個原因,我google了一下,網上也有反映這個錯誤的,但是按照他們的方法並不能解決我的問題,於是根據這個錯誤提示,大膽猜測——
解決思路:
“signal僅適用於主線程”,是不是因爲在Flask中起動scrapy爬蟲,請求flask的接口已經佔用了主線程並且阻塞等待爬蟲運行,所以再啓動Scrapy時就會報ValueError: signal only works in main thread
根據這個思路,再啓動Scrapy時,創建一個新的進程,讓Scrapy在此進程中的主線程中運行,如下:
# 啓動scrapy項目文件
def start_crawler_threads(topic_name, rules, start_url):
# 通過start_url得到spider名字
spider_name = get_spider(start_url)
# 獲得當前工作目錄(根目錄)
old_path = os.getcwd()
# 在進程中啓動爬蟲
crawl_threads = Process(target=main_illness.start_crawl, args=(spider_name ,))
crawl_threads.start()
# 啓動完線程以後交給工作目錄還原
set_old_path(old_path)
使用進程後,問題解決
三、subprocess.CalledProcessError: Command '['scrapy', 'crawl', 'zhkw', '-o', 'output.json']' returned non-zero exit status 2.
問題描述:
這個問題是在出現問題二之後,我採用的一個解決方法時報的錯,使用subprocess子進程啓動爬蟲,嘗試解決ValueError: signal only works in main thread
問題分析:
用subprocess來啓動scrapy思路是可行的,只不過在這裏並沒有執行成功,是因爲這是在更改工作空間之前,所以會報錯。但是使用此方法會有一個缺陷,雖然再子進程中啓動了scrapy但是爬蟲依然在阻塞,接口就會一直處於阻塞狀態,所以建議使用多進程解決。
具體關於阻塞在問題四中會詳細介紹
四、接口調用爬蟲就會阻塞
問題描述:
由於本項目是由Flask提供接口來啓動爬蟲,但是爬蟲往往都是運行時間很長,接口肯定是不能一直在等待爬蟲運行完才結束
解決思路:
我們都知道進程和線程都是異步執行的,最開始我的解決辦法就是使用多線程來啓動每個爬蟲,然後啓動線程之後直接返回,爬蟲在線程中繼續運行。但是後來遇到了問題二,所以就結合問題二使用多進程來啓動爬蟲。
以上就是對Flask結合Scrapy中遇到的問題的解決,大家看了還有不清楚的地方,可以私信或者評論,我都會盡力解答。
另外,由於本人技術有限,此博客有何不對的地方歡迎大家指正,謝謝。