This answer is plagiarizer from several sources on StackOverflow — most of them above, but I’ve forgotten the others — sorry. It’s simple and scripts run «as is». For releases you test you script, then copy it to the server and Stop/Start the associated service. And it should work for all scripting languages (Python, Perl, node.js), plus batch scripts such as GitBash, PowerShell, even old DOS bat scripts.
pyGlue is the glue that sits between Windows Services and your script.
'''
A script to create a Windows Service, which, when started, will run an executable with the specified parameters.
Optionally, you can also specify a startup directory
To use this script you MUST define (in class Service)
1. A name for your service (short - preferably no spaces)
2. A display name for your service (the name visibile in Windows Services)
3. A description for your service (long details visible when you inspect the service in Windows Services)
4. The full path of the executable (usually C:/Python38/python.exe or C:WINDOWS/System32/WindowsPowerShell/v1.0/powershell.exe
5. The script which Python or PowerShell will run(or specify None if your executable is standalone - in which case you don't need pyGlue)
6. The startup directory (or specify None)
7. Any parameters for your script (or for your executable if you have no script)
NOTE: This does not make a portable script.
The associated '_svc_name.exe' in the dist folder will only work if the executable,
(and any optional startup directory) actually exist in those locations on the target system
Usage: 'pyGlue.exe [options] install|update|remove|start [...]|stop|restart [...]|debug [...]'
Options for 'install' and 'update' commands only:
--username domain\\username : The Username the service is to run under
--password password : The password for the username
--startup [manual|auto|disabled|delayed] : How the service starts, default = manual
--interactive : Allow the service to interact with the desktop.
--perfmonini file: .ini file to use for registering performance monitor data
--perfmondll file: .dll file to use when querying the service for performance data, default = perfmondata.dll
Options for 'start' and 'stop' commands only:
--wait seconds: Wait for the service to actually start or stop.
If you specify --wait with the 'stop' option, the service and all dependent services will be stopped,
each waiting the specified period.
'''
# Import all the modules that make life easy
import servicemanager
import socket
import sys
import win32event
import win32service
import win32serviceutil
import win32evtlogutil
import os
from logging import Formatter, Handler
import logging
import subprocess
# Define the win32api class
class Service (win32serviceutil.ServiceFramework):
# The following variable are edited by the build.sh script
_svc_name_ = "TestService"
_svc_display_name_ = "Test Service"
_svc_description_ = "Test Running Python Scripts as a Service"
service_exe = 'c:/Python27/python.exe'
service_script = None
service_params = []
service_startDir = None
# Initialize the service
def __init__(self, args):
win32serviceutil.ServiceFramework.__init__(self, args)
self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
self.configure_logging()
socket.setdefaulttimeout(60)
# Configure logging to the WINDOWS Event logs
def configure_logging(self):
self.formatter = Formatter('%(message)s')
self.handler = logHandler()
self.handler.setFormatter(self.formatter)
self.logger = logging.getLogger()
self.logger.addHandler(self.handler)
self.logger.setLevel(logging.INFO)
# Stop the service
def SvcStop(self):
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
win32event.SetEvent(self.hWaitStop)
# Run the service
def SvcDoRun(self):
self.main()
# This is the service
def main(self):
# Log that we are starting
servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE, servicemanager.PYS_SERVICE_STARTED,
(self._svc_name_, ''))
# Fire off the real process that does the real work
logging.info('%s - about to call Popen() to run %s %s %s', self._svc_name_, self.service_exe, self.service_script, self.service_params)
self.process = subprocess.Popen([self.service_exe, self.service_script] + self.service_params, shell=False, cwd=self.service_startDir)
logging.info('%s - started process %d', self._svc_name_, self.process.pid)
# Wait until WINDOWS kills us - retrigger the wait for stop every 60 seconds
rc = None
while rc != win32event.WAIT_OBJECT_0:
rc = win32event.WaitForSingleObject(self.hWaitStop, (1 * 60 * 1000))
# Shut down the real process and exit
logging.info('%s - is terminating process %d', self._svc_name_, self.process.pid)
self.process.terminate()
logging.info('%s - is exiting', self._svc_name_)
class logHandler(Handler):
'''
Emit a log record to the WINDOWS Event log
'''
def emit(self, record):
servicemanager.LogInfoMsg(record.getMessage())
# The main code
if __name__ == '__main__':
'''
Create a Windows Service, which, when started, will run an executable with the specified parameters.
'''
# Check that configuration contains valid values just in case this service has accidentally
# been moved to a server where things are in different places
if not os.path.isfile(Service.service_exe):
print('Executable file({!s}) does not exist'.format(Service.service_exe), file=sys.stderr)
sys.exit(0)
if not os.access(Service.service_exe, os.X_OK):
print('Executable file({!s}) is not executable'.format(Service.service_exe), file=sys.stderr)
sys.exit(0)
# Check that any optional startup directory exists
if (Service.service_startDir is not None) and (not os.path.isdir(Service.service_startDir)):
print('Start up directory({!s}) does not exist'.format(Service.service_startDir), file=sys.stderr)
sys.exit(0)
if len(sys.argv) == 1:
servicemanager.Initialize()
servicemanager.PrepareToHostSingle(Service)
servicemanager.StartServiceCtrlDispatcher()
else:
# install/update/remove/start/stop/restart or debug the service
# One of those command line options must be specified
win32serviceutil.HandleCommandLine(Service)
Now there’s a bit of editing and you don’t want all your services called ‘pyGlue’. So there’s a script (build.sh) to plug in the bits and create a customized ‘pyGlue’ and create an ‘.exe’. It is this ‘.exe’ which gets installed as a Windows Service. Once installed you can set it to run automatically.
#!/bin/sh
# This script build a Windows Service that will install/start/stop/remove a service that runs a script
# That is, executes Python to run a Python script, or PowerShell to run a PowerShell script, etc
if [ $# -lt 6 ]; then
echo "Usage: build.sh Name Display Description Executable Script StartupDir [Params]..."
exit 0
fi
name=$1
display=$2
desc=$3
exe=$4
script=$5
startDir=$6
shift; shift; shift; shift; shift; shift
params=
while [ $# -gt 0 ]; do
if [ "${params}" != "" ]; then
params="${params}, "
fi
params="${params}'$1'"
shift
done
cat pyGlue.py | sed -e "s/pyGlue/${name}/g" | \
sed -e "/_svc_name_ =/s?=.*?= '${name}'?" | \
sed -e "/_svc_display_name_ =/s?=.*?= '${display}'?" | \
sed -e "/_svc_description_ =/s?=.*?= '${desc}'?" | \
sed -e "/service_exe =/s?=.*?= '$exe'?" | \
sed -e "/service_script =/s?=.*?= '$script'?" | \
sed -e "/service_params =/s?=.*?= [${params}]?" | \
sed -e "/service_startDir =/s?=.*?= '${startDir}'?" > ${name}.py
cxfreeze ${name}.py --include-modules=win32timezone
Installation — copy the ‘.exe’ the server and the script to the specified folder. Run the ‘.exe’, as Administrator, with the ‘install’ option. Open Windows Services, as Adminstrator, and start you service. For upgrade, just copy the new version of the script and Stop/Start the service.
Now every server is different — different installations of Python, different folder structures. I maintain a folder for every server, with a copy of pyGlue.py and build.sh. And I create a ‘serverBuild.sh’ script for rebuilding all the service on that server.
# A script to build all the script based Services on this PC
sh build.sh AutoCode 'AutoCode Medical Documents' 'Autocode Medical Documents to SNOMED_CT and AIHW codes' C:/Python38/python.exe autocode.py C:/Users/russell/Documents/autocoding -S -T
The web is a living, breathing organism – it constantly adapts and changes. In this dynamic environment, gathering time-sensitive data such as E-commerce listings only once is useless as it quickly becomes obsolete. To be competitive, you must keep your data fresh and run your web scraping scripts repeatedly and regularly.
The easiest way is to run a script in the background. In other words, run it as a service. Fortunately, no matter the operating system in use – Linux or Windows – you have great tools at your disposal. This guide will detail the process in a few simple steps.
Preparing a Python script for Linux
In this article, information from a list of book URLs will be scraped. When the process reaches the end of the list, it loops over and refreshes the data again and again.
First, make a request and retrieve the HTML content of a page. Use the Requests module to do so:
urls = [
'https://books.toscrape.com/catalogue/sapiens-a-brief-history-of-humankind_996/index.html',
'https://books.toscrape.com/catalogue/shakespeares-sonnets_989/index.html',
'https://books.toscrape.com/catalogue/sharp-objects_997/index.html',
]
index = 0
while True:
url = urls[index % len(urls)]
index += 1
print('Scraping url', url)
response = requests.get(url)
Once the content is retrieved, parse it using the Beautiful Soup library:
soup = BeautifulSoup(response.content, 'html.parser')
book_name = soup.select_one('.product_main').h1.text
rows = soup.select('.table.table-striped tr')
product_info = {row.th.text: row.td.text for row in rows}
Make sure your data directory-to-be already exists, and then save book information there in JSON format.
Protip: make sure to use the pathlib module to automatically convert Python path separators into a format compatible with both Windows and Linux systems.
data_folder = Path('./data')
data_folder.mkdir(parents=True, exist_ok=True)
json_file_name = re.sub('[: ]', '-', book_name)
json_file_path = data_folder / f'{json_file_name}.json'
with open(json_file_path, 'w') as book_file:
json.dump(product_info, book_file)
Since this script is long-running and never exits, you must also handle any requests from the operating system attempting to shut down the script. This way, you can finish the current iteration before exiting. To do so, you can define a class that handles the operating system signals:
class SignalHandler:
shutdown_requested = False
def __init__(self):
signal.signal(signal.SIGINT, self.request_shutdown)
signal.signal(signal.SIGTERM, self.request_shutdown)
def request_shutdown(self, *args):
print('Request to shutdown received, stopping')
self.shutdown_requested = True
def can_run(self):
return not self.shutdown_requested
Instead of having a loop condition that never changes (while True), you can ask the newly built SignalHandler whether any shutdown signals have been received:
signal_handler = SignalHandler()
# ...
while signal_handler.can_run():
# run the code only if you don't need to exit
Here’s the code so far:
import json
import re
import signal
from pathlib import Path
import requests
from bs4 import BeautifulSoup
class SignalHandler:
shutdown_requested = False
def __init__(self):
signal.signal(signal.SIGINT, self.request_shutdown)
signal.signal(signal.SIGTERM, self.request_shutdown)
def request_shutdown(self, *args):
print('Request to shutdown received, stopping')
self.shutdown_requested = True
def can_run(self):
return not self.shutdown_requested
signal_handler = SignalHandler()
urls = [
'https://books.toscrape.com/catalogue/sapiens-a-brief-history-of-humankind_996/index.html',
'https://books.toscrape.com/catalogue/shakespeares-sonnets_989/index.html',
'https://books.toscrape.com/catalogue/sharp-objects_997/index.html',
]
index = 0
while signal_handler.can_run():
url = urls[index % len(urls)]
index += 1
print('Scraping url', url)
response = requests.get(url)
soup = BeautifulSoup(response.content, 'html.parser')
book_name = soup.select_one('.product_main').h1.text
rows = soup.select('.table.table-striped tr')
product_info = {row.th.text: row.td.text for row in rows}
data_folder = Path('./data')
data_folder.mkdir(parents=True, exist_ok=True)
json_file_name = re.sub('[\': ]', '-', book_name)
json_file_path = data_folder / f'{json_file_name}.json'
with open(json_file_path, 'w') as book_file:
json.dump(product_info, book_file)
The script will refresh JSON files with newly collected book information.
Running a Linux daemon
If you’re wondering how to run Python script in Linux, there are multiple ways to do it on startup. Many distributions have built-in GUI tools for such purposes.
Let’s use one of the most popular distributions, Linux Mint, as an example. It uses a desktop environment called Cinnamon that provides a startup application utility.
It allows you to add your script and specify a startup delay.
However, this approach doesn’t provide more control over the script. For example, what happens when you need to restart it?
This is where systemd comes in. Systemd is a service manager that allows you to manage user processes using easy-to-read configuration files.
To use systemd, let’s first create a file in the /etc/systemd/system directory:
cd /etc/systemd/system
touch book-scraper.service
Add the following content to the book-scraper.service file using your favorite editor:
[Unit]
Description=A script for scraping the book information
After=syslog.target network.target
[Service]
WorkingDirectory=/home/oxylabs/Scraper
ExecStart=/home/oxylabs/Scraper/venv/bin/python3 scrape.py
Restart=always
RestartSec=120
[Install]
WantedBy=multi-user.target
Here’s the basic rundown of the parameters used in the configuration file:
-
After – ensures you only start your Python script once the network is up.
-
RestartSec – sleep time before restarting the service.
-
Restart – describes what to do if a service exits, is killed, or a timeout is reached.
-
WorkingDirectory – current working directory of the script.
-
ExecStart – the command to execute.
Now, it’s time to tell systemd about the newly created daemon. Run the daemon-reload command:
Then, start your service:
systemctl start book-scraper
And finally, check whether your service is running:
$ systemctl status book-scraper
book-scraper.service - A script for scraping the book information
Loaded: loaded (/etc/systemd/system/book-scraper.service; disabled; vendor preset: enabled)
Active: active (running) since Thu 2022-09-08 15:01:27 EEST; 16min ago
Main PID: 60803 (python3)
Tasks: 1 (limit: 18637)
Memory: 21.3M
CGroup: /system.slice/book-scraper.service
60803 /home/oxylabs/Scraper/venv/bin/python3 scrape.py
Sep 08 15:17:55 laptop python3[60803]: Scraping url https://books.toscrape.com/catalogue/shakespeares-sonnets_989/index.html
Sep 08 15:17:55 laptop python3[60803]: Scraping url https://books.toscrape.com/catalogue/sharp-objects_997/index.html
Sep 08 15:17:55 laptop python3[60803]: Scraping url https://books.toscrape.com/catalogue/sapiens-a-brief-history-of-humankind_996/index.html
Protip: use journalctl -S today -u book-scraper.service to monitor your logs in real-time.
Congrats! Now you can control your service via systemd.
Running a Python script as a Windows service
Running a Python script as a Windows service is not as straightforward as one might expect. Let’s start with the script changes.
To begin, change how the script is executed based on the number of arguments it receives from the command line.
If the script receives a single argument, assume that Windows Service Manager is attempting to start it. It means that you have to run an initialization code. If zero arguments are passed, print some helpful information by using win32serviceutil.HandleCommandLine:
if __name__ == '__main__':
if len(sys.argv) == 1:
servicemanager.Initialize()
servicemanager.PrepareToHostSingle(BookScraperService)
servicemanager.StartServiceCtrlDispatcher()
else:
win32serviceutil.HandleCommandLine(BookScraperService)
Next, extend the special utility class and set some properties. The service name, display name, and description will all be visible in the Windows services utility (services.msc) once your service is up and running.
class BookScraperService(win32serviceutil.ServiceFramework):
_svc_name_ = 'BookScraperService'
_svc_display_name_ = 'BookScraperService'
_svc_description_ = 'Constantly updates the info about books'
Finally, implement the SvcDoRun and SvcStop methods to start and stop the service. Here’s the script so far:
import sys
import servicemanager
import win32event
import win32service
import win32serviceutil
import json
import re
from pathlib import Path
import requests
from bs4 import BeautifulSoup
class BookScraperService(win32serviceutil.ServiceFramework):
_svc_name_ = 'BookScraperService'
_svc_display_name_ = 'BookScraperService'
_svc_description_ = 'Constantly updates the info about books'
def __init__(self, args):
win32serviceutil.ServiceFramework.__init__(self, args)
self.event = win32event.CreateEvent(None, 0, 0, None)
def GetAcceptedControls(self):
result = win32serviceutil.ServiceFramework.GetAcceptedControls(self)
result |= win32service.SERVICE_ACCEPT_PRESHUTDOWN
return result
def SvcDoRun(self):
urls = [
'https://books.toscrape.com/catalogue/sapiens-a-brief-history-of-humankind_996/index.html',
'https://books.toscrape.com/catalogue/shakespeares-sonnets_989/index.html',
'https://books.toscrape.com/catalogue/sharp-objects_997/index.html',
]
index = 0
while True:
result = win32event.WaitForSingleObject(self.event, 5000)
if result == win32event.WAIT_OBJECT_0:
break
url = urls[index % len(urls)]
index += 1
print('Scraping url', url)
response = requests.get(url)
soup = BeautifulSoup(response.content, 'html.parser')
book_name = soup.select_one('.product_main').h1.text
rows = soup.select('.table.table-striped tr')
product_info = {row.th.text: row.td.text for row in rows}
data_folder = Path('C:\\Users\\User\\Scraper\\dist\\scrape\\data')
data_folder.mkdir(parents=True, exist_ok=True)
json_file_name = re.sub('[\': ]', '-', book_name)
json_file_path = data_folder / f'{json_file_name}.json'
with open(json_file_path, 'w') as book_file:
json.dump(product_info, book_file)
def SvcStop(self):
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
win32event.SetEvent(self.event)
if __name__ == '__main__':
if len(sys.argv) == 1:
servicemanager.Initialize()
servicemanager.PrepareToHostSingle(BookScraperService)
servicemanager.StartServiceCtrlDispatcher()
else:
win32serviceutil.HandleCommandLine(BookScraperService)
Now that you have the script, open a Windows terminal of your preference.
Protip: if you’re using Powershell, make sure to include a .exe extension when running binaries to avoid unexpected errors.
Once the terminal is open, change the directory to the location of your script with a virtual environment, for example:
Next, install the experimental Python Windows extensions module, pypiwin32. You’ll also need to run the post-install script:
.\venv\Scripts\pip install pypiwin32
.\venv\Scripts\pywin32_postinstall.py -install
Unfortunately, if you attempt to install your Python script as a Windows service with the current setup, you’ll get the following error:
**** WARNING ****
The executable at "C:\Users\User\Scraper\venv\lib\site-packages\win32\PythonService.exe" is being used as a service.
This executable doesn't have pythonXX.dll and/or pywintypesXX.dll in the same
directory, and they can't be found in the System directory. This is likely to
fail when used in the context of a service.
The exact environment needed will depend on which user runs the service and
where Python is installed. If the service fails to run, this will be why.
NOTE: You should consider copying this executable to the directory where these
DLLs live - "C:\Users\User\Scraper\venv\lib\site-packages\win32" might be a good place.
However, if you follow the instructions of the error output, you’ll be met with a new issue when trying to launch your script:
Error starting service: The service did not respond to the start or control request in a timely fashion.
To solve this issue, you can add the Python libraries and interpreter to the Windows path. Alternatively, bundle your script and all its dependencies into an executable by using pyinstaller:
venv\Scripts\pyinstaller --hiddenimport win32timezone -F scrape.py
The —hiddenimport win32timezone option is critical as the win32timezone module is not explicitly imported but is still needed for the script to run.
Finally, let’s install the script as a service and run it by invoking the executable you’ve built previously:
PS C:\Users\User\Scraper> .\dist\scrape.exe install
Installing service BookScraper
Changing service configuration
Service updated
PS C:\Users\User\Scraper> .\dist\scrape.exe start
Starting service BookScraper
PS C:\Users\User\Scraper>
And that’s it. Now, you can open the Windows services utility and see your new service running.
Protip: you can read more about specific Windows API functions here.
The newly created service is running
Making your life easier by using NSSM on Windows
As evident, you can use win32serviceutil to develop a Windows service. But the process is definitely not that simple – you could even say it sucks! Well, this is where the NSSM (Non-Sucking Service Manager) comes into play.
Let’s simplify the script by only keeping the code that performs web scraping:
import json
import re
from pathlib import Path
import requests
from bs4 import BeautifulSoup
urls = ['https://books.toscrape.com/catalogue/sapiens-a-brief-history-of-humankind_996/index.html',
'https://books.toscrape.com/catalogue/shakespeares-sonnets_989/index.html',
'https://books.toscrape.com/catalogue/sharp-objects_997/index.html', ]
index = 0
while True:
url = urls[index % len(urls)]
index += 1
print('Scraping url', url)
response = requests.get(url)
soup = BeautifulSoup(response.content, 'html.parser')
book_name = soup.select_one('.product_main').h1.text
rows = soup.select('.table.table-striped tr')
product_info = {row.th.text: row.td.text for row in rows}
data_folder = Path('C:\\Users\\User\\Scraper\\data')
data_folder.mkdir(parents=True, exist_ok=True)
json_file_name = re.sub('[\': ]', '-', book_name)
json_file_path = data_folder / f'{json_file_name}.json'
with open(json_file_path, 'w') as book_file:
json.dump(product_info, book_file)
Next, build a binary using pyinstaller:
venv\Scripts\pyinstaller -F simple_scrape.py
Now that you have a binary, it’s time to install NSSM by visiting the official website. Extract it to a folder of your choice and add the folder to your PATH environment variable for convenience.
Then, run the terminal as an admin.
Once the terminal is open, change the directory to your script location:
Finally, install the script using NSSM and start the service:
nssm.exe install SimpleScrape C:\Users\User\Scraper\dist\simple_scrape.exe
nssm.exe start SimpleScrape
Protip: if you have issues, redirect the standard error output of your service to a file to see what went wrong:
nssm set SimpleScrape AppStderr C:\Users\User\Scraper\service-error.log
NSSM ensures that a service is running in the background, and if it doesn’t, you at least get to know why.
Conclusion
Regardless of the operating system, you have various options for setting up Python scripts for recurring web scraping tasks. Whether you need the configurability of systemd, the flexibility of Windows services, or the simplicity of NSSM, be sure to follow this tried & true guide as you navigate their features.
If you are interested in more Python automation solutions for web scraping applications or web scraping with Python, take a look at our blog for various tutorials on all things web scraping. We also offer an advanced solution, Web Scraper API, designed to collect public data from most websites automatically and hassle-free. In addition, you can use a Scheduler feature to schedule multiple web scraping jobs at any frequency you like.
People also ask
What is systemd?
Systemd is a powerful tool for managing processes. It also provides logging and dependency management, for example, starting processes only when a network is available or once a previous service has been started.
What is the difference between systemd and systemctl?
Systemctl is a utility and is part of systemd. Among other things, it allows you to manage services and check their status.
Can I use cron instead of systemd?
You definitely can if it suits your needs. However, if you need more control over the service, systemd is the way to go. It allows you to start services based on certain conditions and is perfect for dealing with long-running scripts. It even allows you to run scripts in a similar way to crontab by using timers.
What’s the difference between a daemon and a service?
The word daemon comes from MIT’s Project MAC, which first coined the term in 1963. It is, by definition, an agent that works tirelessly in the background. Later Windows and Linux adopted the term as an alternative name. Therefore, this article uses the words daemon and service interchangeably.
About the author
Augustas Pelakauskas
Senior Copywriter
Augustas Pelakauskas is a Senior Copywriter at Oxylabs. Coming from an artistic background, he is deeply invested in various creative ventures — the most recent one being writing. After testing his abilities in the field of freelance journalism, he transitioned to tech content creation. When at ease, he enjoys sunny outdoors and active recreation. As it turns out, his bicycle is his fourth best friend.
All information on Oxylabs Blog is provided on an «as is» basis and for informational purposes only. We make no representation and disclaim all liability with respect to your use of any information contained on Oxylabs Blog or any third-party websites that may be linked therein. Before engaging in scraping activities of any kind you should consult your legal advisors and carefully read the particular website’s terms of service or receive a scraping license.
Related articles
IN THIS ARTICLE:
-
Preparing a Python script for Linux
-
Running a Linux daemon
-
Running a Python script as a Windows service
-
Making your life easier by using NSSM on Windows
-
Conclusion
Forget about complex web scraping processes
Choose Oxylabs’ advanced web intelligence collection solutions to gather real-time public data hassle-free.
Introduction
If you want to run a Python script as a Windows service you can use the Non-Sucking Service Manager to create the service.
This application allows you to easily create a service using any executable or batch script that gets
treated as a proper Windows service that you can manage Windows services by running services.msc
or by going to the Services tab from the Task Manager.
The most common use case for running a Python script as a service is to ensure an application is always run
and starts running when you log in.
For example, if you have a web application that you want to stay running whenever the computer is up.
I have used this method to run Flask applications and other utilities.
Setup
Download nssm and extract nssm.exe
.
NSSM is a single file nssm.exe
that does not need any special installation.
For convenience, you may want to place the file inside a directory in your %PATH%
environment variable, otherwise you will need to execute it using the full path.
To learn how to set environment variables in Windows, check out my tutorial
Set Environment Variables in Windows.
You will also need Python installed.
I recommend creating a virtual environment. I usually create at least one virtual environment in
my user’s home directory in a directory named venv
. Then I can be sure Python is available in C:\Users\<my_user>\venv\Scripts\python.exe
.
To learn how to setup a Python virtual environment, check out my tutorial
Python Virtual Environments Tutorial.
IMPORTANT: When running nssm
commands, tou need to run the Command Prompt as an Adminstrator. Right click on Command Prompt and choose Run as Administrator.
Create Windows service to run Python script
To create a custom service, you simply need to run nssm install
and tell it the name of your new service
and what program you want to run. In this case we are calling Python from the virtual environment
and running a Python script from my user’s home directory.
nssm.exe install "MyCustomService" "C:\Users\nanodano\venv\Scripts\python.exe" "C:\Users\nanodano\myscript.py"
You should see a message that says something like:
Service "MyCustomService" installed successfully!
It will default to have Startup type: Automatic. This means it will start automatically when the computer restarts.
Start and stop your custom service
You can use the normal Services manager services.msc
or you can use NSSM from the Command Prompt.
You can start and stop the service like this:
nssm.exe start MyCustomService
nssm.exe stop MyCustomService
Delete the service
If you no longer want the service you can remove it with the following command:
nssm.exe remove "MyCustomService"
Conclusion
After reading this, you should understand how to create your own custom Windows service to run a Python script using NSSM.
You should understand how to create the service and remove it as well as how to start, stop, and restart it.
You should understand how to run a specific Python interpreter to run a Python script.
References
- Non-Sucking Service Manager
- Python
- Set Environment Variables in Windows
- Python Virtual Environments Tutorial
Введение
Примеры
Скрипт Python, который можно запустить как сервис
Модули , используемые в данном примере , являются частью pywin32 (Python для расширений Windows). В зависимости от того, как вы установили Python, вам может потребоваться установить его отдельно.
import win32serviceutil
import win32service
import win32event
import servicemanager
import socket
class AppServerSvc (win32serviceutil.ServiceFramework):
_svc_name_ = "TestService"
_svc_display_name_ = "Test Service"
def __init__(self,args):
win32serviceutil.ServiceFramework.__init__(self,args)
self.hWaitStop = win32event.CreateEvent(None,0,0,None)
socket.setdefaulttimeout(60)
def SvcStop(self):
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
win32event.SetEvent(self.hWaitStop)
def SvcDoRun(self):
servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE,
servicemanager.PYS_SERVICE_STARTED,
(self._svc_name_,''))
self.main()
def main(self):
pass
if __name__ == '__main__':
win32serviceutil.HandleCommandLine(AppServerSvc)
Это просто шаблон. Код вашего приложения, вероятно, вызывающий отдельный скрипт, будет идти в функцию main ().
Вам также нужно будет установить это как сервис. Лучшее решение для этого в данный момент , как представляется, использовать Non-сосущий Service Manager.Это позволяет установить службу и предоставляет графический интерфейс для настройки командной строки, которую выполняет служба. Для Python вы можете сделать это, что создает сервис за один раз:
nssm install MyServiceName c:\python27\python.exe c:\temp\myscript.py
Где my_script.py — это приведенный выше шаблон сценария, измененный для вызова сценария приложения или кода в функции main().Обратите внимание, что сервис не запускает скрипт Python напрямую, он запускает интерпретатор Python и передает ему основной скрипт в командной строке.
В качестве альтернативы вы можете использовать инструменты, предоставленные в Windows Server Resource Kit для вашей версии операционной системы, поэтому создайте службу.
Запуск веб-приложения Flask в качестве службы
Это вариант общего примера. Вам просто нужно импортировать приложение сценарий и вызвать это run()
метод в сервисе main()
функции. В этом случае мы также используем многопроцессорной модуль из — за проблемы с доступом WSGIRequestHandler
.
import win32serviceutil
import win32service
import win32event
import servicemanager
from multiprocessing import Process
from app import app
class Service(win32serviceutil.ServiceFramework):
_svc_name_ = "TestService"
_svc_display_name_ = "Test Service"
_svc_description_ = "Tests Python service framework by receiving and echoing messages over a named pipe"
def __init__(self, *args):
super().__init__(*args)
def SvcStop(self):
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
self.process.terminate()
self.ReportServiceStatus(win32service.SERVICE_STOPPED)
def SvcDoRun(self):
self.process = Process(target=self.main)
self.process.start()
self.process.run()
def main(self):
app.run()
if __name__ == '__main__':
win32serviceutil.HandleCommandLine(Service)
Взято из https://codecamp.ru/a/25130524/318488
Синтаксис
Параметры
Примечания
Steps To Create an Python Windows Service
(1) Copy Paste These Codes to a Python File (e.g. server.py)
import servicemanager
import sys
import win32serviceutil
from mainserver import FlaskServer # Import your code, I've written a module called mainserver which contains FlaskServer Code using OOPs
import threading
import concurrent.futures
import time
class workingthread(threading.Thread):
def __init__(self, quitEvent):
self.quitEvent = quitEvent
self.waitTime = 1
threading.Thread.__init__(self)
def run(self):
try:
# Running start_flask() function on different thread, so that it doesn't blocks the code
executor = concurrent.futures.ThreadPoolExecutor(max_workers=5)
executor.submit(self.start_flask)
except:
pass
# Following Lines are written so that, the program doesn't get quit
# Will Run a Endless While Loop till Stop signal is not received from Windows Service API
while not self.quitEvent.isSet(): # If stop signal is triggered, exit
time.sleep(1)
def start_flask(self):
# This Function contains the actual logic, of windows service
# This is case, we are running our flaskserver
test = FlaskServer()
test.start()
class FlaskService(win32serviceutil.ServiceFramework):
_svc_name_ = "AA Testing"
_svc_display_name_ = "AAA Testing"
_svc_description_ = "This is my service"
def __init__(self, args):
win32serviceutil.ServiceFramework.__init__(self, args)
self.hWaitStop = threading.Event()
self.thread = workingthread(self.hWaitStop)
def SvcStop(self):
self.hWaitStop.set()
def SvcDoRun(self):
self.thread.start()
self.hWaitStop.wait()
self.thread.join()
if __name__ == '__main__':
if len(sys.argv) == 1:
servicemanager.Initialize()
servicemanager.PrepareToHostSingle(FlaskService)
servicemanager.StartServiceCtrlDispatcher()
else:
win32serviceutil.HandleCommandLine(FlaskService)
(2) Install the latest pywin32.exe
- NOTE: If you install pywin32 using pip, then it will not going to install properly, thus will show you some errors,
- Visit https://github.com/mhammond/pywin32/releases
- And Download the latest exe, If you are using Python 32bit then download
pywin32-302.win32-py3.x.exe
- If using Python 64 bit, then download
pywin32-302.win-amd64-py3.x.exe
(3) Compile your server.py using pyinstaller
- Compiling service executable
C:\Users\Pushpender\Desktop> python -m pip install servicemanager
C:\Users\Pushpender\Desktop> pyinstaller --onefile server.py --hidden-import=win32timezone --clean --uac-admin
- Installing & Running service executable (Run CMD as Administrator)
C:\WINDOWS\system32>cd C:\Users\Pushpender\Desktop>
C:\WINDOWS\system32>d:
C:\Users\Pushpender\Desktop>server.exe --startup=auto install # Installing service with startup == Automatic
C:\Users\Pushpender\Desktop>server.exe start # For starting service (You can start from Windows Service or From Task Manager)
C:\Users\Pushpender\Desktop>server.exe stop # For stopping service (You can stop from Windows Service or From Task Manager)
C:\Users\Pushpender\Desktop>server.exe remove # For removing installed service
(4) You can run server.py directly without compiling (Run CMD as Administrator)
C:\WINDOWS\system32>cd C:\Users\Pushpender\Desktop>
C:\WINDOWS\system32>d:
C:\Users\Pushpender\Desktop>python server.py --startup=auto install # Installing service with startup == Automatic
C:\Users\Pushpender\Desktop>python server.py start # For starting service (You can start from Windows Service or From Task Manager)
C:\Users\Pushpender\Desktop>python server.py stop # For stopping service (You can stop from Windows Service or From Task Manager)
C:\Users\Pushpender\Desktop>python server.py remove # For removing installed service
NOTE:
-
You can tweak the above code, for example, you can change the following things
_svc_display_name_ = "AAA Testing" _svc_description_ = "This is my service"
-
Also you can change the classes names of
FlaskService
,workingthread
-
Please change this line in your code
from mainserver import FlaskServer
-
You can change the startup type to lots of things, type
server.exe --help
in order to know more about settings -
If you want to set the StartUp= Manaull, then don’t use
--startup=auto
, while installing service -
If you want to set the StartUp= Automatic (Delayed), then use
--startup=delayed
, while installing service -
Use
--startup
argument beforeinstall
argument -
If you have used any kind of paths in your code such as «/logging/logs.txt», then Make sure to use full/absolute paths in your code, e.g «C:/logging/logs.txt». Because windows will going to call your service from any other path
-
Use this command in order to run your service in debug mode:
server.exe debug
(4) Integrate Windows server with Inno Setup Builder
- If you want to create a Installer which will Install your service at Installion process & will remove it at Uninstall, then
- Add these block of code in your
script.iss
[Run]
Filename: "{app}\{#MyAppExeName}"; StatusMsg: "Installing Windows Service ... "; Parameters: "--startup=delayed install"; Flags: runhidden waituntilterminated
Filename: "{app}\{#MyAppExeName}"; StatusMsg: "Running Windows Service ... "; Parameters: "start"; Flags: runhidden waituntilterminated
[UninstallRun]
Filename: "{app}\{#MyAppExeName}"; Parameters: "remove"; Flags: runhidden