Dieser Artikel ist der 11. Tag von Crawler / Web Scraping Adventskalender 2016.
Im März 2016 wurde ein Buch mit dem Titel Web Scraping with Python veröffentlicht. Es ist ein bisschen Kraft, aber ich habe auch geholfen.
Die in Web Scraping with Python eingeführten Scraping-Tools Selen und Phantomjs, Beim Erstellen eines Scraper besteht das Problem, dass Phantomjs je nach Umgebung nicht geschlossen werden können. Dieses Mal möchte ich den Fall oder das Problem vorstellen.
Führen Sie den folgenden Code aus. Es wird davon ausgegangen, dass Python, Node, Selen und Phantomjs bereits installiert sind.
run.py
from selenium.webdriver.phantomjs.webdriver import WebDriver
browser = WebDriver()
browser.close()
browser.quit()
print('Finished')
Führen Sie diese run.py aus.
$ python run.py
Finished
Wenn es eine Umgebung gibt, in der es ausgeführt werden kann, wird Finished an die Standardausgabe ausgegeben und beendet.
Überprüfen wir nun, ob der Phantomjs-Prozess vorhanden ist. Wenn Sie Probleme haben, sollte der Phantomjs-Prozess zurückgelassen werden.
$ ps aux | grep phantomjs
sximada 74272 100.0 0.0 2432804 2004 s006 S+ 4:41PM 0:00.01 grep --color phantomjs
sximada 74267 0.0 0.7 3647068 59976 s006 S 4:41PM 0:02.01 /usr/local/bin/phantomjs --cookies-file=/var/folders/hx/xp4thw0x7rj15r_2w57_wvfh0000gn/T/tmp2vmrand3 --webdriver=50599
Es ist traurig, nicht wahr?
Es gibt mehrere ähnliche Beiträge zum Stapelüberlauf für dieses Problem, von denen einige wie "pkill phantomjs white" sind. Ich fühle mich ernst.
In der obigen Betriebsumgebung wurde Folgendes verwendet.
Da die Installation von OS, Python und Node alles andere als wichtig ist, werde ich die Erklärung überspringen. Führen Sie für Selen "Pip Install Selen" normalerweise durch.
Das Problem ist Phantomjs. Da es sich um einen Knoten handelt, möchte ich "npm install phantomjs" ausführen.
Die Heimat von Phantomjs ist https://github.com/ariya/phantomjs. Dies ist jedoch nicht etwas, das Sie mit npm eingeben können.
npm install phantomjs
installiert https://github.com/Medium/phantomjs.
Dies ist der NPM-Wrapper für die Installation von Phantomjs, wie Sie in der Beschreibung des Repositorys sehen können.
Ein Wrapper, mit dem Sie Phantomjs mit npm installieren / ausführen können.
Wenn Sie Folgendes tun, werden Sie wahrscheinlich auf Probleme stoßen (obwohl dies in einigen Fällen nicht der Fall ist): Erstellen Sie package.json.
$ npm init .
phantomjs(github.com/Medium/phantomjs)をインストールします。
$ npm install phantomjs
zombie1.py:
from selenium.webdriver.phantomjs.webdriver import WebDriver
browser = WebDriver(executable_path='./node_modules/.bin/phantomjs') # MODIFIED
browser.close()
browser.quit()
print('Finished')
Führen Sie zombie1.py aus.
$ python zombie1.py
Finished
Überprüfen Sie, ob der Prozess bestehen bleibt.
(py3.5.2) $ ps aux | grep phantomjs
sximada 2426 0.0 0.0 2423392 408 s002 R+ 5:51PM 0:00.00 grep --color phantomjs
sximada 2421 0.0 0.6 3645988 46780 s002 S 5:51PM 0:01.56 /usr/local/bin/phantomjs --cookies-file=/var/folders/hx/xp4thw0x7rj15r_2w57_wvfh0000gn/T/tmp8nobgxn7 --webdriver=50641
Hey da ist noch.
Was ist mit Phantomjs, die mit Homebrew installiert wurden?
$ brew install phantomjs
zombie2.py:
from selenium.webdriver.phantomjs.webdriver import WebDriver
browser = WebDriver(executable_path='/usr/local/bin/phantomjs')
browser.close()
browser.quit()
print('Finished')
Führen Sie zombie2.py aus.
$ python zombie2.py
Finished
Überprüfen Sie, ob der Prozess bestehen bleibt.
$ ps aux | grep phantomjs
sximada 3530 0.0 0.0 2432804 796 s002 R+ 6:11PM 0:00.00 grep --color phantomjs
$
Es sind keine mehr übrig. Was ist der Unterschied?
Wenn Sie mit dem Befehl file prüfen, ist phantomjs / usr / local / bin / phantomjs
, das von homebrew eingegeben wurde, eine ausführbare Binärdatei.
Der Einfachheit halber ist diese direkte Methode die Binärversion.
$ file /usr/local/bin/phantomjs
/usr/local/bin/phantomjs: Mach-O 64-bit executable x86_64
Wenn Sie dagegen die mit npm mit dem Befehl file eingegebenen Phantomjs überprüfen, handelt es sich um eine Textdatei. Der Einfachheit halber ist diese direkte Methode die npm-Version.
$ file node_modules/.bin/phantomjs
node_modules/.bin/phantomjs: a /usr/bin/env node script text executable, ASCII text
Der Inhalt wird im Skript von nodejs wie folgt beschrieben.
#!/usr/bin/env node
/**
* Script that will execute the downloaded phantomjs binary. stdio are
* forwarded to and from the child process.
*
* The following is for an ugly hack to avoid a problem where the installer
* finds the bin script npm creates during global installation.
*
* {NPM_INSTALL_MARKER}
*/
var path = require('path')
var spawn = require('child_process').spawn
var binPath = require(path.join(__dirname, '..', 'lib', 'phantomjs')).path
var args = process.argv.slice(2)
// For Node 0.6 compatibility, pipe the streams manually, instead of using
// `{ stdio: 'inherit' }`.
var cp = spawn(binPath, args)
cp.stdout.pipe(process.stdout)
cp.stderr.pipe(process.stderr)
process.stdin.pipe(cp.stdin)
cp.on('error', function (err) {
console.error('Error executing phantom at', binPath)
console.error(err.stack)
})
cp.on('exit', function(code){
// Wait few ms for error to be printed.
setTimeout(function(){
process.exit(code)
}, 20)
});
process.on('SIGTERM', function() {
cp.kill('SIGTERM')
process.exit(1)
})
Es scheint, dass Sie die Binärdatei als untergeordneten Prozess mit "var cp = spawn (binPath, args)" ausführen. Gegen Ende gibt es einen Handler für "SIGTERM", und wenn SIGTERM kommt, scheint er "SIGTERM" an den untergeordneten Prozess zu senden und zu beenden.
Wenn Sie Selen mit der Binärversion und der npm-Version starten, hat der Prozess die folgende Struktur.
Binäre Version:
$ pstree 3812
-+= 03812 sximada python zombie2.py
\--- 03815 sximada /usr/local/bin/phantomjs --cookies-file=/var/folders/hx/xp4thw0x7rj15r_2w57_wvfh0000gn/T/tmpu8trzjh0 --webdriver=50761
npm Version:
$ pstree 3701
-+= 03701 sximada python zombie1.py
\-+- 03704 sximada node ./node_modules/.bin/phantomjs --cookies-file=/var/folders/hx/xp4thw0x7rj15r_2w57_wvfh0000gn/T/tmp9c0y1sj7 --webdriver=50747
\--- 03705 sximada /usr/local/bin/phantomjs --cookies-file=/var/folders/hx/xp4thw0x7rj15r_2w57_wvfh0000gn/T/tmp9c0y1sj7 --webdriver=50747
Wenn man sich den Prozess anschaut, scheint es, dass immer noch Enkelkinder am Ende hängen.
$ pstree 4537
-+= 04537 sximada python zombie1.py
\-+- 04540 sximada node ./node_modules/.bin/phantomjs --cookies-file=/var/folders/hx/xp4thw0x7rj15r_2w57_wvfh0000gn/T/tmpg1eq1xst --webdriver=51406
\--- 04541 sximada /usr/local/bin/phantomjs --cookies-file=/var/folders/hx/xp4thw0x7rj15r_2w57_wvfh0000gn/T/tmpg1eq1xst --webdriver=51406
$ ps aux | grep phantomjs
sximada 4554 0.0 0.0 2432804 632 s003 R+ 6:50PM 0:00.00 grep --color phantomjs
sximada 4541 0.0 0.6 3646488 47532 s002 S 6:49PM 0:05.84 /usr/local/bin/phantomjs --cookies-file=/var/folders/hx/xp4thw0x7rj15r_2w57_wvfh0000gn/T/tmpg1eq1xst --webdriver=51406
Nach dem Ausführen von node ./node_modules/.bin/phantomjs --cookies-file = / var / folders / hx / xp4thw0x7rj15r_2w57_wvfh0000gn / T / tmpg1eq1xst --webdriver = 51406
Versuchen Sie es mit "kill -KILL".
$ node ./node_modules/.bin/phantomjs --cookies-file=/var/folders/hx/xp4thw0x7rj15r_2w57_wvfh0000gn/T/tmpoazqtmx7 --webdriver=51448
[INFO - 2016-12-10T09:57:42.829Z] GhostDriver - Main - running on port 51448
Wenn der Prozess beginnt, töte ihn mit SIGKILL.
$ ps -ef | grep phantom
501 4662 763 0 6:57PM ttys002 0:00.12 node ./node_modules/.bin/phantomjs --cookies-file=/var/folders/hx/xp4thw0x7rj15r_2w57_wvfh0000gn/T/tmpoazqtmx7 --webdriver=51448
501 4663 4662 0 6:57PM ttys002 0:01.73 /usr/local/bin/phantomjs --cookies-file=/var/folders/hx/xp4thw0x7rj15r_2w57_wvfh0000gn/T/tmpoazqtmx7 --webdriver=51448
501 4666 764 0 6:57PM ttys003 0:00.00 grep --color phantom
$ kill -KILL 4662
$ ps -ef | grep phantom
501 4663 1 0 6:57PM ttys002 0:03.63 /usr/local/bin/phantomjs --cookies-file=/var/folders/hx/xp4thw0x7rj15r_2w57_wvfh0000gn/T/tmpoazqtmx7 --webdriver=51448
501 4670 764 0 6:58PM ttys003 0:00.00 grep --color phantom
Ich habe es reproduziert. Es ist deswegen. Sendet Selen SIGKILL beim Herunterfahren?
Von hier aus verwenden wir pdb.set_trace (), um herauszufinden, was Python mit dem Debugger tut. Lassen Sie uns pdb in zombie1.py einfügen und sehen, wie es funktioniert.
from selenium.webdriver.phantomjs.webdriver import WebDriver
browser = WebDriver(executable_path='./node_modules/.bin/phantomjs')
import pdb; pdb.set_trace()
browser.close()
browser.quit()
print('Finished')
Bei Betrachtung des Vorgangs scheint eine HTTP-Anforderung an selenium / webdriver / remote / remote_connection.py (470) _request () gesendet zu werden.
464 if password_manager:
465 opener = url_request.build_opener(url_request.HTTPRedirectHandler(),
466 HttpErrorHandler(),
467 url_request.HTTPBasicAuthHandler(password_manager))
468 else:
469 opener = url_request.build_opener(url_request.HTTPRedirectHandler(),
470 HttpErrorHandler())
471 -> resp = opener.open(request, timeout=self._timeout)
472 statuscode = resp.code
Die Anfrage, die wir senden, lautet wie folgt.
-> request = Request(url, data=body.encode('utf-8'), method=method)
(Pdb) p url
'http://127.0.0.1:51524/wd/hub/session/57277cb0-bec1-11e6-a0b1-31edd9b29650/window'
(Pdb) p body
'{"sessionId": "57277cb0-bec1-11e6-a0b1-31edd9b29650"}'
(Pdb) p method
'DELETE'
(Pdb)
Davon abgesehen scheint es nichts Besonderes zu tun.
Was ist mit quit ()?
Während des Prozesses "self.service.stop ()" von selenium / webdriver / phantomjs / webdriver.py (76) quit () gab es einen Ort, an den SIGTERM und SIGKILL gesendet wurden.
selenium/webdriver/common/service.py(154)stop():
(Pdb) list
149 stream.close()
150 except AttributeError:
151 pass
152 self.process.terminate()
153 self.process.kill()
154 -> self.process.wait()
155 self.process = None
156 except OSError:
157 # kill may not be available under windows environment
158 pass
159
Soweit ich den Code gelesen habe, sende ich SIGKILL nach dem Senden von SIGTERM.
Die npm-Version hatte jedoch einen SIGTERM-Signalhandler. Ist dieses SIGKILL überhaupt notwendig?
Kommentieren Sie als Test self.process.kill ()
aus und führen Sie es aus.
selenium/webdriver/common/service.py:
self.process.terminate()
# self.process.kill() ##Auskommentieren
self.process.wait()
Ich werde das machen.
$ python zombie1.py
Finished
$ ps aux | grep phantomjs
sximada 5270 0.0 0.0 2424612 500 s002 R+ 7:34PM 0:00.00 grep --color phantomjs
$
Es sind keine Prozesse mehr vorhanden. Es scheint, dass der untergeordnete Prozess beendet wurde und der Enkelprozess aufgrund von "self.process.kill ()" bestehen bleibt.
Anscheinend hat die npm-Version von phantomjs SIGTERM gesendet, das von self.process.terminate () gesendet wurde. Es scheint, dass der Handler von SIGKILL getötet wurde, bevor SIGTERM zum Enkelprozess geschickt wurde. Ich fühle mich wie ich mitten in der Grenze festsitze.
Es scheint gut zu sein, wenn Sie nicht durch npm gehen, also nicht npm install phantomjs
,
Sie können es mit Homebrew installieren oder von http://phantomjs.org/download.html löschen.
Ich habe das Gefühl, einen Umweg gemacht zu haben. Ich bete, dass niemand den gleichen Umweg macht.
Recommended Posts