Cet article est le 11ème jour du Calendrier de l'Avent Crawler / Web Scraping 2016.
En mars 2016, un livre intitulé Web Scraping with Python a été publié. C'est un peu de puissance, mais j'ai aussi aidé.
Les outils de grattage sélénium et phantomjs introduits dans Web scraping with Python, Lors de la création d'un grattoir, il y a un problème en raison du fait que phantomjs ne peut pas être fermé en fonction de l'environnement. Cette fois, je voudrais présenter le cas ou le problème.
Exécutez le code suivant. On suppose que Python, Node, sélénium et phantomjs sont déjà installés.
run.py
from selenium.webdriver.phantomjs.webdriver import WebDriver
browser = WebDriver()
browser.close()
browser.quit()
print('Finished')
Exécutez ce run.py.
$ python run.py
Finished
S'il existe un environnement dans lequel il peut être exécuté, il affichera Terminé sur la sortie standard et quittera.
Vérifions maintenant si le processus phantomjs existe. Si vous rencontrez des problèmes, le processus phantomjs doit être laissé pour compte.
$ 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
C'est triste, n'est-ce pas.
Il y a plusieurs articles similaires sur stackoverflow pour ce problème, dont certains sont comme pkill phantomjs white
.
J'ai l'impression que c'est sérieux.
L'environnement d'exploitation ci-dessus utilisait ce qui suit.
Étant donné que l'installation du système d'exploitation, de Python et de Node est loin d'être essentielle, je vais sauter l'explication. Pour le sélénium, faites "pip install selenium" normalement.
Le problème est phantomjs. Puisqu'il s'agit d'un nœud, je veux faire npm install phantomjs
.
La maison de phantomjs est https://github.com/ariya/phantomjs. Mais ce n'est pas quelque chose que vous pouvez mettre en place avec npm.
npm install phantomjs
installe https://github.com/Medium/phantomjs.
C'est le wrapper NPM pour l'installation de phantomjs
, comme vous pouvez le voir dans la description du référentiel.
Un wrapper qui vous permet d'installer / d'exécuter phantomjs avec npm.
En fait, vous pouvez rencontrer des problèmes (bien que dans certains cas ce ne soit pas le cas): Créez 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')
Exécutez zombie1.py.
$ python zombie1.py
Finished
Vérifiez si le processus persiste.
(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
Hé, il reste.
Qu'en est-il des phantomjs installés avec homebrew?
$ 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')
Exécutez zombie2.py.
$ python zombie2.py
Finished
Vérifiez si le processus persiste.
$ ps aux | grep phantomjs
sximada 3530 0.0 0.0 2432804 796 s002 R+ 6:11PM 0:00.00 grep --color phantomjs
$
Il n'en reste plus. Quelle est la différence?
Si vous vérifiez avec la commande file, phantomjs / usr / local / bin / phantomjs
entré par homebrew est un fichier binaire exécutable.
Pour plus de commodité, cette méthode directe est la version binaire.
$ file /usr/local/bin/phantomjs
/usr/local/bin/phantomjs: Mach-O 64-bit executable x86_64
Par contre, si vous vérifiez les phantomjs saisis avec npm avec la commande file, c'est un fichier texte. Pour plus de commodité, cette méthode directe est la version npm.
$ file node_modules/.bin/phantomjs
node_modules/.bin/phantomjs: a /usr/bin/env node script text executable, ASCII text
Le contenu est décrit comme suit dans le script de nodejs.
#!/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)
})
Il semble que vous exécutiez le binaire en tant que processus enfant avec var cp = spawn (binPath, args)
.
Il y a un gestionnaire pour SIGTERM
vers la fin, et quand SIGTERM arrive, il semble envoyer SIGTERM
au processus fils et quitter.
Si vous démarrez sélénium en utilisant la version binaire et la version npm, le processus aura la structure suivante.
Version binaire:
$ 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
version npm:
$ 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
En regardant le processus, il semble qu'il y ait encore des petits-enfants suspendus au fond de cela.
$ 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
Après avoir exécuté node ./node_modules/.bin/phantomjs --cookies-file = / var / folders / hx / xp4thw0x7rj15r_2w57_wvfh0000gn / T / tmpg1eq1xst --webdriver = 51406
Essayez kill -KILL
sur ce processus.
$ 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
Lorsque le processus démarre, tuez-le avec 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
Je l'ai reproduit. C'est à cause de ça. Le sélénium envoie-t-il SIGKILL à l'arrêt?
À partir de là, nous utiliserons pdb.set_trace () pour découvrir ce que fait Python en utilisant le débogueur. Mettons pdb dans zombie1.py et voyons comment cela fonctionne.
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')
En regardant l'opération, il semble qu'une requête HTTP soit envoyée à selenium / webdriver / remote / remote_connection.py (470) _request ().
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
La demande que nous envoyons est la suivante.
-> 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)
À part cela, il ne semble rien faire de particulier.
Et quit ()?
Dans le processus de self.service.stop ()
de selenium / webdriver / phantomjs / webdriver.py (76) quit (), il y avait un endroit où SIGTERM et SIGKILL ont été envoyés.
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
Pour autant que j'ai lu le code, j'envoie SIGKILL après avoir envoyé SIGTERM.
Cependant, la version npm avait un gestionnaire de signaux SIGTERM. Ce SIGKILL est-il nécessaire en premier lieu?
Comme test, commentez self.process.kill ()
et exécutez-le.
selenium/webdriver/common/service.py:
self.process.terminate()
# self.process.kill() ##Commenter
self.process.wait()
Je le ferai.
$ 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
$
Il n'y a plus de processus. Il semble que le processus enfant a été tué et que le processus petit-enfant reste à cause de self.process.kill ()
.
Apparemment, la version npm de phantomjs a envoyé SIGTERM envoyé par self.process.terminate ()
.
Il semble que le gestionnaire a été tué par SIGKILL avant d'envoyer SIGTERM au processus petit-enfant.
J'ai l'impression d'être coincé au milieu de la frontière.
Cela semble bien si vous ne passez pas par npm, alors n'installez pas npm phantomjs, Vous pouvez l'installer avec homebrew ou le supprimer depuis http://phantomjs.org/download.html.
J'ai l'impression d'avoir fait un détour. Je prie pour que personne ne fasse le même détour.
Recommended Posts