Cuối năm rảnh rỗi, đang ngồi lướt facebook thì thấy bà chị nhắn tin nhờ kiếm ít dữ liệu doanh nghiệp để spam sale cũng đang rảnh nên thôi thì cũng giúp đỡ tý, lâu rồi không code cũng ngứa nghề
Đầu tiên giới thiệu qua trang https://vinabiz.org/ là một trang cho phép chúng ta xem thông tin của các doanh nghiệp ở VN theo tỉnh, thành phố, nghành nghề,… tất nhiên thông tin cơ bản thôi nhưng qua đủ để các sale đi spam
Để “cào” dữ liệu từ một trang web trươc hết phải xác định những thông tin sau:
- Cấu trúc dữ liệu cần lấy (các trường dữ liệu, kiểu dữ liệu)
- Các trang web load data về browser, thường thì trang web sẽ gọi API hoặc render trực tiếp vào trang HTML
- Dùng công nghệ gì để “cào”
- Code, code và code
Ngoài ra trong quá trình thực hiện sẽ vướng phải những vấn đề khác (vd. các trick để trang web chống lại các cào-er, …) mình sẽ nói rõ ở phần sau. Chúng ta bắt đầu theo các bước từ trên xuống dưới . Bài này mình sẽ dùng Python để code, còn tại sao lại là Python thì xin trả lời là nó nhanh, dễ code, dễ chạy, dùng text editor cũng code được mà không cần vác IDE ra (ở đây mình dùng Visual Studio Code của MS)
B1: Xác định cấu trúc dữ liệu cần lấy
Thông tin của một doanh nghiệp trên https://vinabiz.org/ sẽ có dạng như sau:
Tạo một Class với các attributes là các trường mình quan tâm
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <span class="token keyword">class</span> <span class="token class-name">Company</span><span class="token punctuation">:</span> official_name <span class="token operator">=</span> <span class="token string">''</span> trading_name <span class="token operator">=</span> <span class="token string">''</span> bussiness_code <span class="token operator">=</span> <span class="token string">''</span> date_of_license <span class="token operator">=</span> <span class="token string">''</span> start_working_date <span class="token operator">=</span> <span class="token string">''</span> status <span class="token operator">=</span> <span class="token string">''</span> address <span class="token operator">=</span> <span class="token string">''</span> phone <span class="token operator">=</span> <span class="token string">''</span> email <span class="token operator">=</span> <span class="token string">''</span> director <span class="token operator">=</span> <span class="token string">''</span> director_phone <span class="token operator">=</span> <span class="token string">''</span> accountant <span class="token operator">=</span> <span class="token string">''</span> accountant_phone <span class="token operator">=</span> <span class="token string">''</span> business_lines <span class="token operator">=</span> <span class="token string">''</span> <span class="token keyword">def</span> <span class="token function">__repr__</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span> <span class="token keyword">return</span> <span class="token builtin">str</span><span class="token punctuation">(</span>self<span class="token punctuation">.</span>__dict__<span class="token punctuation">)</span> |
Okay chúng ta đã xong bước đầu tiên, đơn giản nhỉ
B2: Xác định cách trang web load data về
Cách đơn giản nhất và mình cũng mong muốn nhất là trang web load data về bằng cách gọi API. Bắt đầu mở Dev tool lên và check phần Network. Và thật là đ*o còn gì đen hơn, trang này có gọi API thật, nhưng mà lại là API Ads Xác định phát này lại ngồi bóc HTML sml rồi!!!!
Tiếp tục dùng Network để tìm những request trả về data, và may mắn là chỉ cần Get request tới url của công ty là chúng ta đã có đầy đủ dữ liệu
Thế là lại xong bước 2, có vẻ dễ hơn ăn cháo
B3: xác định công nghệ để “cào”
Ngôn ngữ thì mình đã nói trước là sử dụng Python, còn để bóc tách HMTL thì có một thư viện đã quá nổi tiếng và quen thuộc rồi đó là Beautiful Soup. Thư viện này hỗ trợ rất nhiều ngôn ngữ khác nhau và tất nhiên là có cho Python, mình sẽ không nói về cách setup nữa, các bạn có thể xem thêm tại đây
B4: Đầy đủ thông tin rồi bắt tay vào cào thôi
Cấu hình Logging để có thể sử dụng tính năng log có sẵn của Python, rất tiện cho việc debug (thật ra dùng print() cũng được mà mình thích màu mè )
1 2 3 4 | <span class="token keyword">import</span> logging log_format <span class="token operator">=</span> <span class="token string">'[%(levelname)s] - %(message)s'</span> logging<span class="token punctuation">.</span>basicConfig<span class="token punctuation">(</span>level<span class="token operator">=</span><span class="token string">'INFO'</span><span class="token punctuation">,</span> <span class="token builtin">format</span><span class="token operator">=</span>log_format<span class="token punctuation">)</span> |
Nhìn vào cách paging ta có thể thấy trang web này paging bằng path ở cuối url, số trang tương ứng luôn với path đó. Vì vậy ta cần xác định page bắt đầu và page kết thúc muốn lấy dữ liệu.
Bắt đầu khai báo các arguments cần thiết:
- url: link chứa danh sách các doanh nghiệp, có thể lấy theo tỉnh, thành, quận, huyện bla bla bla bla
- start: trang bắt đầu lấy
- end: trang cuối cùng cần lấy
- out: file lưu dữ liệu cào được
1 2 3 4 5 6 7 8 9 | <span class="token keyword">import</span> argparse parser <span class="token operator">=</span> argparse<span class="token punctuation">.</span>ArgumentParser<span class="token punctuation">(</span><span class="token punctuation">)</span> parser<span class="token punctuation">.</span>add_argument<span class="token punctuation">(</span><span class="token string">"--url"</span><span class="token punctuation">,</span> <span class="token string">"-u"</span><span class="token punctuation">,</span> <span class="token builtin">help</span><span class="token operator">=</span><span class="token string">"base url"</span><span class="token punctuation">)</span> parser<span class="token punctuation">.</span>add_argument<span class="token punctuation">(</span><span class="token string">"--start"</span><span class="token punctuation">,</span> <span class="token string">"-s"</span><span class="token punctuation">,</span> <span class="token builtin">help</span><span class="token operator">=</span><span class="token string">"start page"</span><span class="token punctuation">)</span> parser<span class="token punctuation">.</span>add_argument<span class="token punctuation">(</span><span class="token string">"--end"</span><span class="token punctuation">,</span> <span class="token string">"-e"</span><span class="token punctuation">,</span> <span class="token builtin">help</span><span class="token operator">=</span><span class="token string">"end page"</span><span class="token punctuation">)</span> parser<span class="token punctuation">.</span>add_argument<span class="token punctuation">(</span><span class="token string">"--out"</span><span class="token punctuation">,</span> <span class="token string">"-o"</span><span class="token punctuation">,</span> <span class="token builtin">help</span><span class="token operator">=</span><span class="token string">"output file"</span><span class="token punctuation">)</span> args <span class="token operator">=</span> parser<span class="token punctuation">.</span>parse_args<span class="token punctuation">(</span><span class="token punctuation">)</span> |
Nếu muốn màu mè có thể thêm phần validate cho các urguments phần này phụ nên mình if else cho lẹ
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <span class="token keyword">def</span> <span class="token function">check_input</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span> <span class="token keyword">if</span> args<span class="token punctuation">.</span>url <span class="token keyword">is</span> <span class="token boolean">None</span><span class="token punctuation">:</span> logging<span class="token punctuation">.</span>error<span class="token punctuation">(</span><span class="token string">'Please enter base url'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> sys<span class="token punctuation">.</span>exit<span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span> <span class="token keyword">if</span> args<span class="token punctuation">.</span>start <span class="token keyword">is</span> <span class="token boolean">None</span><span class="token punctuation">:</span> logging<span class="token punctuation">.</span>error<span class="token punctuation">(</span><span class="token string">'Please enter start page'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token builtin">int</span><span class="token punctuation">(</span>args<span class="token punctuation">.</span>start<span class="token punctuation">)</span> <span class="token operator"><=</span> <span class="token number">0</span><span class="token punctuation">:</span> logging<span class="token punctuation">.</span>error<span class="token punctuation">(</span><span class="token string">'Please enter start page > 0'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> sys<span class="token punctuation">.</span>exit<span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span> <span class="token keyword">if</span> args<span class="token punctuation">.</span>end <span class="token keyword">is</span> <span class="token boolean">None</span><span class="token punctuation">:</span> logging<span class="token punctuation">.</span>error<span class="token punctuation">(</span><span class="token string">'Please enter end page'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> sys<span class="token punctuation">.</span>exit<span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span> <span class="token keyword">if</span> <span class="token builtin">int</span><span class="token punctuation">(</span>args<span class="token punctuation">.</span>start<span class="token punctuation">)</span> <span class="token operator">></span> <span class="token builtin">int</span><span class="token punctuation">(</span>args<span class="token punctuation">.</span>end<span class="token punctuation">)</span><span class="token punctuation">:</span> logging<span class="token punctuation">.</span>error<span class="token punctuation">(</span><span class="token string">'Please enter start page < end page'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> sys<span class="token punctuation">.</span>exit<span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span> <span class="token keyword">if</span> args<span class="token punctuation">.</span>out <span class="token keyword">is</span> <span class="token boolean">None</span><span class="token punctuation">:</span> logging<span class="token punctuation">.</span>error<span class="token punctuation">(</span><span class="token string">'Please enter output file'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> sys<span class="token punctuation">.</span>exit<span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span> |
Luồng crawl chính sẽ tiền hành như sau:
- Request lên url chứa danh sách doanh nghiệp -> lấy được danh sách link chứa thông tin chi tiết của từng doanh nghiệp
- Request tiếp tơi link chứa thông tin doanh nghiệp đã lấy được ở bước trên -> bóc tách HTML trả về để lấy các thông tin cần thiết
- Sau khi lấy hết các thông tin các doanh nghiệp page này thì tiếp tục request tới page tiếp theo và lặp lại các bước bóc tách.
Để thực hiện các HTTP request ở đây mình sử dụng thư viện Requests của Python. Sau khi cài đặt để sử dụng chỉ cần import vào file code là được.
1 2 | <span class="token keyword">import</span> requests |
Hàm lấy ra danh sách url chứa thông tin chi tiết doanh nghiệp theo page. Danh sách doanh nghiệp được lưu bởi một danh sách các </div> có classs “row margin-right-15 margin-left-10”. Sau khi request lên và nhận được HTML mình sử dụng Beautiful Soup để lọc ra tất cả các div đó và lấy giá trị của thuộc tính href của thẻ <a> nằm trong đó, chính là link chứa thông tin chi tiết của doanh nghiệp.
1 2 3 4 5 6 7 8 9 10 11 12 13 | <span class="token keyword">def</span> <span class="token function">request_list_company</span><span class="token punctuation">(</span>page<span class="token punctuation">)</span><span class="token punctuation">:</span> company_url_list <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span> logging<span class="token punctuation">.</span>info<span class="token punctuation">(</span><span class="token string">"getting list of company in page "</span> <span class="token operator">+</span> <span class="token builtin">str</span><span class="token punctuation">(</span>page<span class="token punctuation">)</span><span class="token punctuation">)</span> url <span class="token operator">=</span> args<span class="token punctuation">.</span>url <span class="token keyword">if</span> <span class="token builtin">int</span><span class="token punctuation">(</span>page<span class="token punctuation">)</span> <span class="token operator">></span> <span class="token number">1</span> <span class="token punctuation">:</span> url <span class="token operator">=</span> url <span class="token operator">+</span> <span class="token builtin">str</span><span class="token punctuation">(</span>page<span class="token punctuation">)</span> response <span class="token operator">=</span> requests<span class="token punctuation">.</span>get<span class="token punctuation">(</span>url<span class="token punctuation">)</span> soup <span class="token operator">=</span> BeautifulSoup<span class="token punctuation">(</span>response<span class="token punctuation">.</span>content<span class="token punctuation">,</span> <span class="token string">'html.parser'</span><span class="token punctuation">)</span> list_of_company_div <span class="token operator">=</span> soup<span class="token punctuation">.</span>find_all<span class="token punctuation">(</span><span class="token string">"div"</span><span class="token punctuation">,</span> class_<span class="token operator">=</span> <span class="token string">"row margin-right-15 margin-left-10"</span><span class="token punctuation">)</span> <span class="token keyword">for</span> company_div <span class="token keyword">in</span> list_of_company_div<span class="token punctuation">:</span> <span class="token keyword">if</span> company_div<span class="token punctuation">.</span>find<span class="token punctuation">(</span><span class="token string">'a'</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token string">'href'</span><span class="token punctuation">]</span> <span class="token punctuation">:</span> company_url_list<span class="token punctuation">.</span>append<span class="token punctuation">(</span>company_div<span class="token punctuation">.</span>find<span class="token punctuation">(</span><span class="token string">'a'</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token string">'href'</span><span class="token punctuation">]</span><span class="token punctuation">)</span> logging<span class="token punctuation">.</span>info<span class="token punctuation">(</span><span class="token string">'Get total '</span> <span class="token operator">+</span> <span class="token builtin">str</span><span class="token punctuation">(</span><span class="token builtin">len</span><span class="token punctuation">(</span>company_url_list<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">' company url'</span><span class="token punctuation">)</span> <span class="token keyword">return</span> company_url_list |
Khi đã có danh sách url chi tiết của các doanh nghiệp thì mình sử dụng một vòng lặp đơn giản và viết 1 hàm để lấy ra thông tin chi tiết của mỗi doanh nghiệp. Khi làm đến đây mình phát hiện ra một điều khá thú vị, nếu không login thì khi truy cập vào trang chi tiết một số thông tin như email, số điện thoại giám đốc, bla bla sẽ không hiển thị. Đây là một trick khá vui của trang web, và mình đi tìm cách bypass nó.
Trong request gửi lên mình phát hiện ra server sẽ check loggin session của user thông qua cookies được đính kèm.
Để bypass đơn giản mình đăng nhập vào trang web sau đó khai báo một cái cookies ứng với session đang đăng nhập và gửi kèm theo request
1 2 3 4 5 6 7 | cookie <span class="token operator">=</span> <span class="token string">'__cfduid=dba2b91eb8eca08fdd298...'</span> <span class="token keyword">def</span> <span class="token function">get_company_details</span><span class="token punctuation">(</span>url<span class="token punctuation">)</span><span class="token punctuation">:</span> url <span class="token operator">=</span> <span class="token string">'https://vinabiz.org/'</span> <span class="token operator">+</span> url logging<span class="token punctuation">.</span>info<span class="token punctuation">(</span><span class="token string">'Get company details in '</span> <span class="token operator">+</span> url<span class="token punctuation">)</span> response <span class="token operator">=</span> requests<span class="token punctuation">.</span>get<span class="token punctuation">(</span>url<span class="token punctuation">,</span> headers<span class="token operator">=</span><span class="token punctuation">{</span><span class="token string">'Cookie'</span><span class="token punctuation">:</span> cookie<span class="token punctuation">}</span><span class="token punctuation">)</span> |
Phần response chính là phần html trả về chứa thông tin, tiếp theo ra viết hàm để bóc tách thông tin từ đống html này.
Data được đặt trong một table với class “table table-bordered”, các thông tin ứng với từng hàng và cột trong table đó.
Viết một hàm để bóc tách dữ liệu của table này với input là response trả về từ hàm get_company_detail(url) và return một object Company như đã khai báo ở đầu bài.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <span class="token keyword">def</span> <span class="token function">parse_company_detail</span><span class="token punctuation">(</span>rows<span class="token punctuation">)</span><span class="token punctuation">:</span> emailCode <span class="token operator">=</span> <span class="token boolean">None</span> company <span class="token operator">=</span> Company<span class="token punctuation">(</span><span class="token punctuation">)</span> company<span class="token punctuation">.</span>official_name <span class="token operator">=</span> rows<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">.</span>find_all<span class="token punctuation">(</span><span class="token string">'td'</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">.</span>get_text<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>strip<span class="token punctuation">(</span><span class="token punctuation">)</span> company<span class="token punctuation">.</span>trading_name <span class="token operator">=</span> rows<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">.</span>find_all<span class="token punctuation">(</span><span class="token string">'td'</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">3</span><span class="token punctuation">]</span><span class="token punctuation">.</span>get_text<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>strip<span class="token punctuation">(</span><span class="token punctuation">)</span> company<span class="token punctuation">.</span>bussiness_code <span class="token operator">=</span> rows<span class="token punctuation">[</span><span class="token number">2</span><span class="token punctuation">]</span><span class="token punctuation">.</span>find_all<span class="token punctuation">(</span><span class="token string">'td'</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">.</span>get_text<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>strip<span class="token punctuation">(</span><span class="token punctuation">)</span> company<span class="token punctuation">.</span>date_of_license <span class="token operator">=</span> rows<span class="token punctuation">[</span><span class="token number">2</span><span class="token punctuation">]</span><span class="token punctuation">.</span>find_all<span class="token punctuation">(</span><span class="token string">'td'</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">3</span><span class="token punctuation">]</span><span class="token punctuation">.</span>get_text<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>strip<span class="token punctuation">(</span><span class="token punctuation">)</span> company<span class="token punctuation">.</span>start_working_date <span class="token operator">=</span> rows<span class="token punctuation">[</span><span class="token number">3</span><span class="token punctuation">]</span><span class="token punctuation">.</span>find_all<span class="token punctuation">(</span><span class="token string">'td'</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">3</span><span class="token punctuation">]</span><span class="token punctuation">.</span>get_text<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>strip<span class="token punctuation">(</span><span class="token punctuation">)</span> company<span class="token punctuation">.</span>status <span class="token operator">=</span> rows<span class="token punctuation">[</span><span class="token number">4</span><span class="token punctuation">]</span><span class="token punctuation">.</span>find_all<span class="token punctuation">(</span><span class="token string">'td'</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">.</span>find_all<span class="token punctuation">(</span><span class="token string">'div'</span><span class="token punctuation">,</span> class_<span class="token operator">=</span><span class="token string">'alert alert-success fade in'</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">.</span>get_text<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>strip<span class="token punctuation">(</span><span class="token punctuation">)</span> company<span class="token punctuation">.</span>address <span class="token operator">=</span> rows<span class="token punctuation">[</span><span class="token number">7</span><span class="token punctuation">]</span><span class="token punctuation">.</span>find_all<span class="token punctuation">(</span><span class="token string">'td'</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">.</span>get_text<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>strip<span class="token punctuation">(</span><span class="token punctuation">)</span> company<span class="token punctuation">.</span>phone <span class="token operator">=</span> rows<span class="token punctuation">[</span><span class="token number">8</span><span class="token punctuation">]</span><span class="token punctuation">.</span>find_all<span class="token punctuation">(</span><span class="token string">'td'</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">.</span>get_text<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>strip<span class="token punctuation">(</span><span class="token punctuation">)</span> company<span class="token punctuation">.</span>phone <span class="token operator">=</span> rows<span class="token punctuation">[</span><span class="token number">9</span><span class="token punctuation">]</span><span class="token punctuation">.</span>find_all<span class="token punctuation">(</span><span class="token string">'td'</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">.</span>get_text<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>strip<span class="token punctuation">(</span><span class="token punctuation">)</span> company<span class="token punctuation">.</span>director <span class="token operator">=</span> rows<span class="token punctuation">[</span><span class="token number">12</span><span class="token punctuation">]</span><span class="token punctuation">.</span>find_all<span class="token punctuation">(</span><span class="token string">'td'</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">.</span>get_text<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>strip<span class="token punctuation">(</span><span class="token punctuation">)</span> company<span class="token punctuation">.</span>director_phone <span class="token operator">=</span> rows<span class="token punctuation">[</span><span class="token number">12</span><span class="token punctuation">]</span><span class="token punctuation">.</span>find_all<span class="token punctuation">(</span><span class="token string">'td'</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">.</span>get_text<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>strip<span class="token punctuation">(</span><span class="token punctuation">)</span> company<span class="token punctuation">.</span>accountant <span class="token operator">=</span> rows<span class="token punctuation">[</span><span class="token number">14</span><span class="token punctuation">]</span><span class="token punctuation">.</span>find_all<span class="token punctuation">(</span><span class="token string">'td'</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">.</span>get_text<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>strip<span class="token punctuation">(</span><span class="token punctuation">)</span> company<span class="token punctuation">.</span>accountant_phone <span class="token operator">=</span> rows<span class="token punctuation">[</span><span class="token number">14</span><span class="token punctuation">]</span><span class="token punctuation">.</span>find_all<span class="token punctuation">(</span><span class="token string">'td'</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">3</span><span class="token punctuation">]</span><span class="token punctuation">.</span>get_text<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>strip<span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">return</span> company |
Sau khi làm phần này thì mình cứ nghĩ vậy là xong, nhưng đ*o Khi log phần data bóc được ra thì thấy email bị mã hóa thành [email protected]. Nhìn lại vào thẻ chứa thông tin email thì thấy email được mã hóa ở dạng sau
1 2 3 4 | <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>/cdn-cgi/l/email-protection#2e4d4140495a574a435e004d4100425a4a6e49434f4742004d4143<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>__cf_email__<span class="token punctuation">"</span></span> <span class="token attr-name">data-cfemail</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>c1a2aeafa6b5b8a5acb1efa2aeefadb5a581a6aca0a8adefa2aeac<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>[email<span class="token entity" title=" ">&#160;</span>protected]<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span>; |
Vì khi truy cập bằng browser thì email vẫn hiện ra bình thường -> suy nghĩ email này được decode bởi js phía client sau khi load trang. Tiếp tục lần mò mình phát hiện ra file email-decode.js Đây chính là cái chúng ta cần
Nhưng vấn đề là đây là code javascript, vậy lại cần một bước convert qua python code. Trong quá trình convert mình phát hiện ra một số hàm không liên quan tới việc decode mà chỉ là sửa các phần tử html sau khi decode. Sau khi convert email-decode.js qua Python ta sẽ được:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | <span class="token keyword">import</span> urllib<span class="token punctuation">.</span>parse <span class="token keyword">def</span> <span class="token function">r</span><span class="token punctuation">(</span>e<span class="token punctuation">,</span> t<span class="token punctuation">)</span><span class="token punctuation">:</span> r <span class="token operator">=</span> e<span class="token punctuation">[</span>t<span class="token punctuation">:</span>t<span class="token operator">+</span><span class="token number">2</span><span class="token punctuation">]</span> <span class="token keyword">return</span> <span class="token builtin">int</span><span class="token punctuation">(</span>r<span class="token punctuation">,</span> base<span class="token operator">=</span><span class="token number">16</span><span class="token punctuation">)</span> <span class="token keyword">def</span> <span class="token function">decode</span><span class="token punctuation">(</span>n<span class="token punctuation">,</span> c<span class="token punctuation">)</span><span class="token punctuation">:</span> o <span class="token operator">=</span> <span class="token string">''</span> a <span class="token operator">=</span> r<span class="token punctuation">(</span>n<span class="token punctuation">,</span> c<span class="token punctuation">)</span> i <span class="token operator">=</span> c <span class="token operator">+</span> <span class="token number">2</span> xs <span class="token operator">=</span> i <span class="token keyword">for</span> x <span class="token keyword">in</span> <span class="token builtin">range</span><span class="token punctuation">(</span>i<span class="token punctuation">,</span> <span class="token builtin">len</span><span class="token punctuation">(</span>n<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">:</span> <span class="token keyword">if</span> xs <span class="token keyword">in</span> <span class="token builtin">range</span><span class="token punctuation">(</span>i<span class="token punctuation">,</span> <span class="token builtin">len</span><span class="token punctuation">(</span>n<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">:</span> l <span class="token operator">=</span> r<span class="token punctuation">(</span>n<span class="token punctuation">,</span> xs<span class="token punctuation">)</span> <span class="token operator">^</span> a o <span class="token operator">+=</span> <span class="token builtin">chr</span><span class="token punctuation">(</span>l<span class="token punctuation">)</span> xs <span class="token operator">=</span> xs <span class="token operator">+</span> <span class="token number">2</span> <span class="token keyword">else</span><span class="token punctuation">:</span> <span class="token keyword">break</span> <span class="token keyword">try</span><span class="token punctuation">:</span> o <span class="token operator">=</span> urllib<span class="token punctuation">.</span>parse<span class="token punctuation">.</span>unquote<span class="token punctuation">(</span>urllib<span class="token punctuation">.</span>parse<span class="token punctuation">.</span>quote<span class="token punctuation">(</span>o<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">return</span> o <span class="token keyword">except</span> Exception <span class="token keyword">as</span> e<span class="token punctuation">:</span> logging<span class="token punctuation">.</span>error<span class="token punctuation">(</span><span class="token builtin">str</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span><span class="token punctuation">)</span> |
Sau khi có hàm để decode email thì sửa lại set thông tin email của object Company như sau:
1 2 3 4 | <span class="token keyword">if</span> rows<span class="token punctuation">[</span><span class="token number">9</span><span class="token punctuation">]</span><span class="token punctuation">.</span>find_all<span class="token punctuation">(</span><span class="token string">'td'</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">.</span>find<span class="token punctuation">(</span><span class="token string">'span'</span><span class="token punctuation">,</span> class_<span class="token operator">=</span><span class="token string">'__cf_email__'</span><span class="token punctuation">)</span><span class="token punctuation">:</span> emailCode <span class="token operator">=</span> rows<span class="token punctuation">[</span><span class="token number">9</span><span class="token punctuation">]</span><span class="token punctuation">.</span>find_all<span class="token punctuation">(</span><span class="token string">'td'</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">.</span>find<span class="token punctuation">(</span><span class="token string">'span'</span><span class="token punctuation">,</span> class_<span class="token operator">=</span><span class="token string">'__cf_email__'</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token string">'data-cfemail'</span><span class="token punctuation">]</span> <span class="token keyword">if</span> emailCode <span class="token keyword">is</span> <span class="token operator">not</span> <span class="token boolean">None</span><span class="token punctuation">:</span> company<span class="token punctuation">.</span>email <span class="token operator">=</span> decode<span class="token punctuation">(</span>emailCode<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token keyword">else</span><span class="token punctuation">:</span> company<span class="token punctuation">.</span>email <span class="token operator">=</span> <span class="token string">''</span> |
Hoàn thiện hàm get_company_detail(url)
1 2 3 4 5 6 7 8 9 | <span class="token keyword">def</span> <span class="token function">get_company_details</span><span class="token punctuation">(</span>url<span class="token punctuation">)</span><span class="token punctuation">:</span> url <span class="token operator">=</span> <span class="token string">'https://vinabiz.org/'</span> <span class="token operator">+</span> url logging<span class="token punctuation">.</span>info<span class="token punctuation">(</span><span class="token string">'Get company details in '</span> <span class="token operator">+</span> url<span class="token punctuation">)</span> response <span class="token operator">=</span> requests<span class="token punctuation">.</span>get<span class="token punctuation">(</span>url<span class="token punctuation">,</span> headers<span class="token operator">=</span><span class="token punctuation">{</span><span class="token string">'Cookie'</span><span class="token punctuation">:</span> cookie<span class="token punctuation">}</span><span class="token punctuation">)</span> soup <span class="token operator">=</span> BeautifulSoup<span class="token punctuation">(</span>response<span class="token punctuation">.</span>content<span class="token punctuation">,</span> <span class="token string">'html.parser'</span><span class="token punctuation">)</span> rows <span class="token operator">=</span> soup<span class="token punctuation">.</span>find_all<span class="token punctuation">(</span><span class="token string">"table"</span><span class="token punctuation">,</span> class_<span class="token operator">=</span> <span class="token string">"table table-bordered"</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">.</span>find_all<span class="token punctuation">(</span><span class="token string">'tr'</span><span class="token punctuation">)</span> company <span class="token operator">=</span> parse_company_detail<span class="token punctuation">(</span>rows<span class="token punctuation">)</span> company_arr<span class="token punctuation">.</span>append<span class="token punctuation">(</span>company<span class="token punctuation">)</span> |
Luồng craw dữ liệu hoàn chỉnh, dữ liệu doanh nghiệp sẽ được lưu trong list của Company object với biến company_arr
1 2 3 4 5 6 7 8 | <span class="token keyword">def</span> <span class="token function">craw</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span> company_arr<span class="token punctuation">.</span>clear<span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">for</span> i <span class="token keyword">in</span> <span class="token builtin">range</span><span class="token punctuation">(</span><span class="token builtin">int</span><span class="token punctuation">(</span>args<span class="token punctuation">.</span>start<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token builtin">int</span><span class="token punctuation">(</span>args<span class="token punctuation">.</span>end<span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">:</span> company_url_list <span class="token operator">=</span> request_list_company<span class="token punctuation">(</span>i<span class="token punctuation">)</span> <span class="token keyword">for</span> company_url <span class="token keyword">in</span> company_url_list<span class="token punctuation">:</span> get_company_details<span class="token punctuation">(</span>company_url<span class="token punctuation">)</span> logging<span class="token punctuation">.</span>info<span class="token punctuation">(</span><span class="token string">'Get information of total '</span> <span class="token operator">+</span> <span class="token builtin">str</span><span class="token punctuation">(</span><span class="token builtin">len</span><span class="token punctuation">(</span>company_arr<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">' companies'</span><span class="token punctuation">)</span> |
Việc cuối cùng là ghi dữ liệu ra file excel
Để đọc/ghi excel mình hay sử dụng thư viện python xlwt các bạn có thể đọc cách cài đặt và sử dụng ở trang chủ.
Đầu tiên cần một hàm để ghi header cho file
1 2 3 4 5 6 7 | <span class="token keyword">def</span> <span class="token function">write_sheet_header</span><span class="token punctuation">(</span>sheet<span class="token punctuation">)</span><span class="token punctuation">:</span> sheet_header <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">'Tên chính thức'</span><span class="token punctuation">,</span> <span class="token string">'Tên giao dịch'</span><span class="token punctuation">,</span> <span class="token string">'Mã doanh nghiệp'</span><span class="token punctuation">,</span> <span class="token string">'Ngày cấp'</span><span class="token punctuation">,</span> <span class="token string">'Ngày bắt đầu hoạt động'</span><span class="token punctuation">,</span> <span class="token string">'Trạng thái'</span><span class="token punctuation">,</span> <span class="token string">'Địa chỉ'</span><span class="token punctuation">,</span> <span class="token string">'Điện thoại'</span><span class="token punctuation">,</span> <span class="token string">'Email'</span><span class="token punctuation">,</span> <span class="token string">'Giám đốc'</span><span class="token punctuation">,</span> <span class="token string">'SĐT giám đốc'</span><span class="token punctuation">,</span> <span class="token string">'Kế toán'</span><span class="token punctuation">,</span> <span class="token string">'SĐT kế toán'</span><span class="token punctuation">,</span> <span class="token string">'Nghành nghề'</span><span class="token punctuation">]</span> <span class="token keyword">for</span> header <span class="token keyword">in</span> sheet_header<span class="token punctuation">:</span> sheet<span class="token punctuation">.</span>write<span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> sheet_header<span class="token punctuation">.</span>index<span class="token punctuation">(</span>header<span class="token punctuation">)</span><span class="token punctuation">,</span> header<span class="token punctuation">)</span> |
Tiếp theo viết hàm ghi data vào file từ list company_arr
1 2 3 4 5 6 7 | <span class="token keyword">def</span> <span class="token function">write_sheet_data</span><span class="token punctuation">(</span>sheet<span class="token punctuation">,</span> data<span class="token punctuation">)</span><span class="token punctuation">:</span> <span class="token keyword">for</span> company <span class="token keyword">in</span> data<span class="token punctuation">:</span> attributes_arr <span class="token operator">=</span> <span class="token builtin">list</span><span class="token punctuation">(</span>company<span class="token punctuation">.</span>__dict__<span class="token punctuation">.</span>keys<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">print</span><span class="token punctuation">(</span>attributes_arr<span class="token punctuation">)</span> <span class="token keyword">for</span> att <span class="token keyword">in</span> attributes_arr<span class="token punctuation">:</span> sheet<span class="token punctuation">.</span>write<span class="token punctuation">(</span>data<span class="token punctuation">.</span>index<span class="token punctuation">(</span>company<span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">,</span> attributes_arr<span class="token punctuation">.</span>index<span class="token punctuation">(</span>att<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token builtin">str</span><span class="token punctuation">(</span><span class="token builtin">getattr</span><span class="token punctuation">(</span>company<span class="token punctuation">,</span> att<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span> |
Hoàn chỉnh hàm ghi dữ liệu
1 2 3 4 5 6 7 8 9 10 | <span class="token keyword">def</span> <span class="token function">write_result</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span><span class="token punctuation">:</span> <span class="token builtin">file</span> <span class="token operator">=</span> args<span class="token punctuation">.</span>out <span class="token operator">+</span> <span class="token string">'.xls'</span> logging<span class="token punctuation">.</span>info<span class="token punctuation">(</span><span class="token string">'Save result to file'</span><span class="token punctuation">)</span> wb <span class="token operator">=</span> Workbook<span class="token punctuation">(</span><span class="token punctuation">)</span> sheet <span class="token operator">=</span> wb<span class="token punctuation">.</span>add_sheet<span class="token punctuation">(</span><span class="token string">'Data'</span><span class="token punctuation">)</span> write_sheet_header<span class="token punctuation">(</span>sheet<span class="token punctuation">)</span> write_sheet_data<span class="token punctuation">(</span>sheet<span class="token punctuation">,</span> data<span class="token punctuation">)</span> wb<span class="token punctuation">.</span>save<span class="token punctuation">(</span><span class="token builtin">file</span><span class="token punctuation">)</span> logging<span class="token punctuation">.</span>info<span class="token punctuation">(</span><span class="token string">'Saved to '</span> <span class="token operator">+</span> <span class="token builtin">file</span><span class="token punctuation">)</span> |
Mọi mọi việc đã xong 99%, mình viết thêm 1 hàm main chạy khi ta gọi file
1 2 3 4 5 6 7 | <span class="token keyword">def</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span> check_input<span class="token punctuation">(</span><span class="token punctuation">)</span> craw<span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">if</span> __name__<span class="token operator">==</span> <span class="token string">"__main__"</span><span class="token punctuation">:</span> main<span class="token punctuation">(</span><span class="token punctuation">)</span> |
Và bây giờ, chạy thử và xem kết quả nhé
Mở terminal và gõ
Kết quả
Vậy là xong =)) giờ các bạn có thể dùng thông tin đi làm gì thì làm
Full source tại đây, hy vọng các bạn sử dụng đừng spam quá nhiều