Psing login avatar
Psing login

Deprecated

Pricing

Pay per usage

Go to Store
Psing login

Psing login

Deprecated

Developed by

Ayush Suresh

Ayush Suresh

Maintained by Community

Psing login

0.0 (0)

Pricing

Pay per usage

0

Total users

1

Monthly users

1

Last modified

8 months ago

.actor/Dockerfile

# Use Apify base image with Python and Playwright
FROM apify/actor-python-playwright
# Copy all source code to the default working directory
COPY ./src /app
# Install Python dependencies
RUN pip install requests
# Run the actor script
CMD ["python", "/app/main.py"]

.actor/actor.json

{
"name": "my-actor",
"version": "0.1",
"description": "This is my Apify actor for login automation.",
"actorSpecification": 1,
"input": "./input_schema.json",
"main": "src/main.py"
}

.actor/input_schema.json

{
"schemaVersion": 1,
"title": "My Actor Input Schema",
"type": "object",
"properties": {
"start_url": {
"type": "string",
"title": "Start URL",
"description": "The URL to start the scraping process.",
"editor": "textfield"
},
"company_id": {
"type": "string",
"title": "Company ID",
"description": "ID of the company.",
"editor": "textfield"
},
"username": {
"type": "string",
"title": "Username",
"description": "Username for login.",
"editor": "textfield"
},
"password": {
"type": "string",
"title": "Password",
"description": "Password for login.",
"editor": "textfield"
},
"patient_id": {
"type": "string",
"title": "Patient ID",
"description": "ID of the patient.",
"editor": "textfield"
},
"type_id": {
"type": "string",
"title": "Type ID",
"description": "ID for the appointment type.",
"editor": "textfield"
},
"user_id": {
"type": "string",
"title": "User ID",
"description": "ID of the user making the appointment.",
"editor": "textfield"
},
"reason": {
"type": "string",
"title": "Reason",
"description": "Reason for the appointment.",
"editor": "textfield"
},
"room": {
"type": "string",
"title": "Room",
"description": "Room number for the appointment.",
"editor": "textfield"
},
"appointment_date": {
"type": "string",
"title": "Appointment Date",
"description": "Date of the appointment.",
"editor": "datepicker"
},
"time": {
"type": "string",
"title": "Appointment Start Time",
"description": "Start time of the appointment.",
"editor": "textfield"
},
"time_end": {
"type": "string",
"title": "Appointment End Time",
"description": "End time of the appointment.",
"editor": "textfield"
},
"useRealEndTime": {
"type": "boolean",
"title": "Use Real End Time",
"description": "Whether to use the real end time for the appointment.",
"editor": "checkbox",
"default": true
}
},
"required": ["start_url", "username", "password", "patient_id", "type_id", "user_id", "reason", "room", "appointment_date", "time", "time_end", "useRealEndTime"]
}

src/__main__.py

1import asyncio
2from .main import main
3
4# Execute the Actor entry point
5asyncio.run(main())

src/main.py

1import logging
2import json
3import asyncio
4import requests
5from playwright.async_api import async_playwright
6from apify import Actor # Import the Actor module
7
8logging.basicConfig(level=logging.INFO)
9
10async def main():
11 # Initialize the actor
12 await Actor.init()
13
14 # Load input parameters from Apify input
15 actor_input = await Actor.get_input() or {}
16
17 # Log the input data for debugging
18 logging.info(f'Input data: {actor_input}')
19
20 # Extract input parameters from the input JSON
21 start_url = actor_input.get('start_url')
22 company_id = actor_input.get('company_id')
23 username = actor_input.get('username')
24 password = actor_input.get('password')
25 appointment_details = {
26 'patient_id': actor_input.get('patient_id'),
27 'type_id': actor_input.get('type_id'),
28 'user_id': actor_input.get('user_id'),
29 'reason': actor_input.get('reason'),
30 'room': actor_input.get('room'),
31 'appointment_date': actor_input.get('appointment_date'),
32 'time': actor_input.get('time'),
33 'time_end': actor_input.get('time_end'),
34 'useRealEndTime': actor_input.get('useRealEndTime')
35 }
36
37 # Log extracted appointment details
38 logging.info(f'Appointment Details: {appointment_details}')
39
40 # Ensure all input fields have values, raise an error if any critical field is missing
41 if not all([start_url, company_id, username, password, appointment_details['patient_id'],
42 appointment_details['type_id'], appointment_details['user_id'],
43 appointment_details['reason'], appointment_details['room'],
44 appointment_details['appointment_date'], appointment_details['time'],
45 appointment_details['time_end']]):
46 logging.error("Missing required input fields!")
47 return
48
49 async with async_playwright() as p:
50 browser = await p.chromium.launch(headless=True)
51 context = await browser.new_context()
52 page = await context.new_page()
53
54 # Initialize variables to hold request headers
55 sec_ch_ua = None
56 sec_ch_ua_mobile = None
57
58 # Capture request headers when a request is made
59 def extract_headers(headers):
60 nonlocal sec_ch_ua, sec_ch_ua_mobile
61 sec_ch_ua = headers.get('sec-ch-ua')
62 sec_ch_ua_mobile = headers.get('sec-ch-ua-mobile')
63
64 page.on('request', lambda request: extract_headers(request.headers))
65
66 logging.info(f'Navigating to {start_url}...')
67 await page.goto(start_url)
68
69 # Fill in the login form
70 await page.fill('#company_id', company_id)
71 await page.fill('#username', username)
72 await page.fill('#password', password)
73 await page.click('#btnLogin')
74
75 # Wait for navigation to the dashboard
76 await page.wait_for_url('**/dashboard', timeout=15000)
77 logging.info('Login successful! On the dashboard page.')
78
79 # Extract cookies
80 cookies = await context.cookies()
81 logging.info(f'Cookies: {cookies}')
82 cookie_string = "; ".join([f"{cookie['name']}={cookie['value']}" for cookie in cookies])
83 logging.info(f'Cookie String: {cookie_string}')
84
85 # Extract XSRF token
86 xsrf_token = next((cookie['value'] for cookie in cookies if cookie['name'] == 'XSRF-TOKEN'), None)
87 logging.info(f'XSRF Token: {xsrf_token}')
88
89 # Extract user agent
90 user_agent = await page.evaluate("() => navigator.userAgent")
91 logging.info(f'User Agent: {user_agent}')
92
93 # Log sec-ch-ua and sec-ch-ua-mobile
94 logging.info(f'SEC-CH-UA: {sec_ch_ua}')
95 logging.info(f'SEC-CH-UA-Mobile: {sec_ch_ua_mobile}')
96
97 # Prepare the request headers dynamically
98 request_url = 'https://us.idexxneo.com/appointments/create'
99 headers = {
100 'accept': 'application/json, text/plain, */*',
101 'accept-encoding': 'gzip, deflate, br, zstd',
102 'accept-language': 'en-GB,en;q=0.9',
103 'cookie': cookie_string,
104 'origin': 'https://us.idexxneo.com',
105 'referer': 'https://us.idexxneo.com/schedule/appointments/new?date=' + appointment_details['appointment_date'] + '&time=' + appointment_details['time'],
106 'sec-ch-ua': sec_ch_ua if sec_ch_ua else '',
107 'sec-ch-ua-mobile': sec_ch_ua_mobile if sec_ch_ua_mobile else '',
108 'sec-fetch-dest': 'empty',
109 'sec-fetch-mode': 'cors',
110 'sec-fetch-site': 'same-origin',
111 'sec-gpc': '1',
112 'user-agent': user_agent,
113 'x-neo-core-version': '87.0.0',
114 'x-xsrf-token': xsrf_token
115 }
116
117 # Adjust form data to use 'files' for multipart data
118 files = {
119 'patient_id': (None, appointment_details['patient_id']),
120 'type_id': (None, appointment_details['type_id']),
121 'user_id': (None, appointment_details['user_id']),
122 'reason': (None, appointment_details['reason']),
123 'room': (None, appointment_details['room']),
124 'appointment_date': (None, appointment_details['appointment_date']),
125 'time': (None, appointment_details['time']),
126 'time_end': (None, appointment_details['time_end']),
127 'useRealEndTime': (None, str(appointment_details['useRealEndTime']).lower())
128 }
129
130 # Send the POST request
131 response = requests.post(request_url, headers=headers, files=files)
132
133 # Print the response
134 logging.info(f'Status Code: {response.status_code}')
135 logging.info(f'Response: {response.text}')
136
137 await browser.close()
138
139if __name__ == '__main__':
140 asyncio.run(main())

.dockerignore

.git
.mise.toml
.nvim.lua
storage
# The rest is copied from https://github.com/github/gitignore/blob/main/Python.gitignore
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
.python-version
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
.pdm.toml
.pdm-python
.pdm-build/
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
.idea/

.gitignore

.mise.toml
.nvim.lua
storage
# The rest is copied from https://github.com/github/gitignore/blob/main/Python.gitignore
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
.python-version
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
.pdm.toml
.pdm-python
.pdm-build/
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
.idea/

real.py

1from apify import Actor
2from crawlee.playwright_crawler import PlaywrightCrawler, PlaywrightCrawlingContext
3
4async def main() -> None:
5 """Main entry point for the Apify Actor."""
6 async with Actor:
7 actor_input = await Actor.get_input() or {}
8 start_urls = [url.get('url') for url in actor_input.get('start_urls', [{'url': 'https://us.idexxneo.com/login?'}])]
9
10 if not start_urls:
11 Actor.log.info('No start URLs specified in Actor input, exiting...')
12 await Actor.exit()
13
14 crawler = PlaywrightCrawler(
15 max_requests_per_crawl=1, # We only need to handle one request for the login
16 headless=True,
17 )
18
19 @crawler.router.default_handler
20 async def request_handler(context: PlaywrightCrawlingContext) -> None:
21 url = context.request.url
22 Actor.log.info(f'Navigating to {url}...')
23
24 # Navigate to the login page
25 await context.page.goto(url)
26
27 # Perform login actions
28 await context.page.fill('//*[@id="company_id"]', '7880')
29 await context.page.fill('//*[@id="username"]', 'PSingh')
30 await context.page.fill('//*[@id="password"]', 'Temppass1234!')
31
32 # Click the login button
33 await context.page.click('//*[@id="btnLogin"]')
34
35 # Wait for the page to load after login
36 await context.page.wait_for_load_state('networkidle') # Wait until there are no more network connections for at least 500 ms
37
38 # After logging in, you can extract data or navigate further
39 Actor.log.info('Login successful!')
40
41 # Example: Extracting data after login (you can customize this)
42 data = {
43 'url': context.request.url,
44 'title': await context.page.title(),
45 }
46 await context.push_data(data)
47
48 await crawler.run(start_urls)
49
50if __name__ == '__main__':
51 import asyncio
52 asyncio.run(main())

requirements.txt

1# Feel free to add your Python dependencies below. For formatting guidelines, see:
2# https://pip.pypa.io/en/latest/reference/requirements-file-format/
3
4apify == 2.0.0
5crawlee[playwright]