Giới thiệu
Phần trước mình có giới thiệu qua cách làm việc với django, selenium và docker. Tuy nhiên do nhu cầu công việc nên chúng ta cần chạy song song giữa selenium và django, cùng với đó ta có thể chạy song song nhiều tab driver. Tuy nhiên cách này mình chỉ chạy được trên firefox. Còn chorme nó chỉ chạy một driver. Ở đây mình sẽ sử dụng Selenium Hub và Docker Hub để chạy nhiều node firefox. Đồng thời mình sử dụng Thread của Python để chạy song song tác vụ selenium và django, nhằm ngăn chặn quá trình trang web bị delay khi chạy selenium. Chúng ta sử dụng ajax để thực hiện hành động từ phía server. Ở đây chúng ta cũng có thể sử dụng channels. Nhưng trong bài viết này mình sẽ không sử dụng channels để thực hiện việc đó
Cài đặt
Cài đặt project
pip install django
django-admin startproject app
django-admin startapp product
Khởi tạo project
Django
Sau khi khởi tạo project xong.
app
app
__init__.py
asgi.py
setting.py
urls.py
wsgi.py
product
__init__.py
admin.py
apps.py
models.py
test.py
views.py
manage.py
Chỉnh sửa models.py trong app product Trong file này bạn tạo một model Product.
from django.db import models
from django.contrib.auth.models import User
classProduct(models.Model):
name = models.CharField(max_length=200)def__str__(self)->str:return self.name
Tạo 1 file urls.py và getData.py, thư mục templates và trong templates có product.html trong app product
app
app
__init__.py
asgi.py
setting.py
urls.py
wsgi.py
product
__init__.py
admin.py
apps.py
getData.py
models.py
urls.py
test.py
views.py
templates
product.html
manage.py
Chỉnh sửa urls.py vừa khởi tạo.
from django.conf import settings
from.import views
app_name ='product'
urlpatterns =[
path('', views.home, name ='products'),
ath('crawl/', views.getData),]
Chinh file views.py trong app product. Ở đây chúng ta sử dụng thread để thực hiện lấy dữ liệu.
from django.shortcuts import render
from django.http import JsonResponse
from.models import*from.getData import*from django.shortcuts import render
import threading
defhome(request):
products = Product.objects.all()return render(request,'product.html',{"products": products})defgetDataAjax(request):if request.is_ajax():
data_scrap()defgetData(request):
thread = threading.Thread(target=getDataAjax,args=[request])
thread.daemon =True
thread.start()
thread.join()return JsonResponse({})
Chỉnh sửa urls.py trong app
from django.contrib import admin
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
urlpatterns =[
path('admin/', admin.site.urls),
path('', include('product.urls')),]
Selenium
Chỉnh sửa file getData.py trong app Product. Tại đây chúng ta sử dụng selenium để thực hiện lấy dữ liệu. Và thread để lưu dữ liệu đã lấy vào database.
import time
from selenium import webdriver
from.models import*# Import packagesfrom selenium import webdriver
from bs4 import SoupStrainer
from bs4 import BeautifulSoup
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
from.models import Product
import threading
defdata_scrap():
driver = webdriver.Remote("http://selenium-hub:4444/wd/hub", DesiredCapabilities.FIREFOX)
driver.get("https://github.com/giakinh0823?tab=repositories")
time.sleep(2)
htmlSource = driver.page_source
only_class = SoupStrainer("div",{"id":"user-repositories-list"})
list_product = BeautifulSoup(htmlSource,"html.parser", parse_only=only_class)for item in list_product.findAll("h3",{"class":"wb-break-all"}):
name =str(item.find("a", attrs={"itemprop":"name codeRepository"}).text)print(name)
thread = threading.Thread(target=save_product, args=[name])
thread.daemon=True
thread.start()
time.sleep(3)
driver.quit()defsave_product(name):
product = Product.objects.create(name =name)
product.save()
Templates
Chỉnh sửa file product.html trong templates
<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metahttp-equiv="X-UA-Compatible"content="IE=edge"><metaname="viewport"content="width=device-width, initial-scale=1.0"><title>Document</title><scriptsrc="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script></head><body><h1>Crawl data</h1><buttonid="getdata"onClick="getData()">Get data</button><ul>
{% for product in products %}
<li>{{product.name}}</li>
{% endfor %}
</ul><script>constgetDataAjax=()=>{
$.ajax({
type:"GET",
url:'crawl/',
data:'',
dataType:'json',success:function(data){
console.log(data)}});}</script></body></html>
Docker
Đầu tiên bạn hãy tạo một file Dockerfile và docker-compose.yml trong project app
app
app
__init__.py
asgi.py
setting.py
urls.py
wsgi.py
product
__init__.py
admin.py
apps.py
getData.py
models.py
urls.py
test.py
views.py
templates
product.html
docker-compose.yml
Dockerfile
manage.py
Chỉnh sửa file Dockerfile. Ở đây mình ta ra một thư mục product và coppy requirements.txt vào thư mục đó. Sau đó install requirements.txt và coppy tất cả project của mình vào thư mục product
FROM python:3
ENV PYTHONUNBUFFERED=1
WORKDIR /product
COPY requirements.txt /product/
RUN apt-get update
&& apt-get -y install libpq-dev gcc
RUN pip install -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]
Chỉnh sửa file docker-compose.yml. Ở đây ta có 1 database db có image postgres và database này có tên product và username = admin, password = admin. Chúng ta có selenium-hub với GRID_MAX_SESSION=50, trong selenium-hu ta có node-chorme và node-firefox. Với node-firefox ta có NODE_MAX_SESSION = 20. Hai node-chorme và node-firefox có depends_on là selenium-hub. Ta cũng có web có depends_on là db
version:"3.9"
services:
db:
image: postgres
volumes:-./data/db:/var/lib/postgresql/data
environment:- POSTGRES_DB=product
- POSTGRES_USER=admin
- POSTGRES_PASSWORD=admin
web:
build:.
command: python manage.py runserver 0.0.0.0:8000
volumes:-.:/product
ports:-"8000:8000"
depends_on:- db
selenium-hub:
image: selenium/hub
ports:-"4444:4444"
environment:
GRID_MAX_SESSION:50
GRID_BROWSER_TIMEOUT:300
GRID_TIMEOUT:300
chrome:
image: selenium/node-chrome
depends_on:- selenium-hub
environment:
HUB_PORT_4444_TCP_ADDR: selenium-hub
HUB_PORT_4444_TCP_PORT:4444
NODE_MAX_SESSION:1
NODE_MAX_INSTANCES:1
firefox:
image: selenium/node-firefox
depends_on:- selenium-hub
environment:
HUB_PORT_4444_TCP_ADDR: selenium-hub
HUB_PORT_4444_TCP_PORT:4444
NODE_MAX_SESSION:20
NODE_MAX_INSTANCES:20
Tạo và chỉnh sửa file requirements.txt trong project app
app
app
__init__.py
asgi.py
setting.py
urls.py
wsgi.py
product
__init__.py
admin.py
apps.py
getData.py
models.py
urls.py
test.py
views.py
templates
product.html
docker-compose.yml
Dockerfile
requirements.txt
manage.py
requirements.txt
asgiref==3.4.1
beautifulsoup4==4.9.3
bs4==0.0.1
certifi==2021.5.30
charset-normalizer==2.0.4
colorama==0.4.4
configparser==5.0.2
crayons==0.4.0
Django==3.2.6
ftfy==6.0.3
idna==3.2numpy==1.21.1
pandas==1.3.1
Pillow==8.3.1
python-dateutil==2.8.2
pytz==2021.1requests==2.26.0
selenium==3.141.0
six==1.16.0
soupsieve==2.2.1
sqlparse==0.4.1
urllib3==1.26.6
wcwidth==0.2.5
webdriver-manager==3.4.2
Django>=3.0,<4.0
psycopg2-binary>=2.8psycopg2==2.9.1
Build và Run project
docker-compose build
docker-compose run web python manage.py makemigrations
docker-compose run web python manage.py migrate
docker-compose up
Sau đó bạn vào http://127.0.0.1:8000/ để xem kết quả
Cảm ơn các bạn đã quan tâm
Nguồn: viblo.asia