Python - ตัวช่วยในการค้นหาตั๋วเครื่องบินราคาถูกสำหรับผู้ที่รักการเดินทาง

ผู้เขียนบทความซึ่งแปลอยู่ในปัจจุบันกล่าวว่าเป้าหมายคือการพูดคุยเกี่ยวกับการพัฒนา Web Scraper ใน Python โดยใช้ Selenium ซึ่งค้นหาราคาตั๋วสายการบิน เมื่อค้นหาตั๋ว จะใช้วันที่ที่ยืดหยุ่น (+- 3 วันสัมพันธ์กับวันที่ที่ระบุ) เครื่องมือขูดจะบันทึกผลการค้นหาในไฟล์ Excel และส่งอีเมลพร้อมข้อมูลสรุปสิ่งที่พวกเขาพบให้กับผู้ที่ทำการค้นหา เป้าหมายของโครงการนี้คือเพื่อช่วยให้นักเดินทางค้นพบข้อเสนอที่ดีที่สุด

Python - ตัวช่วยในการค้นหาตั๋วเครื่องบินราคาถูกสำหรับผู้ที่รักการเดินทาง

หากคุณรู้สึกหลงทางในขณะที่เข้าใจเนื้อหา ให้ลองดู นี้ บทความ.

เราจะมองหาอะไร?

คุณสามารถใช้ระบบที่อธิบายไว้ที่นี่ได้อย่างอิสระตามที่คุณต้องการ ตัวอย่างเช่น ฉันใช้มันเพื่อค้นหาทัวร์สุดสัปดาห์และตั๋วไปบ้านเกิดของฉัน หากคุณจริงจังกับการค้นหาตั๋วที่ทำกำไรได้ คุณสามารถเรียกใช้สคริปต์บนเซิร์ฟเวอร์ได้ (simple เซิร์ฟเวอร์สำหรับสิ่งนี้ค่อนข้างเหมาะกับราคา 130 รูเบิลต่อเดือน) และตรวจสอบให้แน่ใจว่าทำงานวันละครั้งหรือสองครั้ง ผลการค้นหาจะถูกส่งถึงคุณทางอีเมล นอกจากนี้ ฉันขอแนะนำให้ตั้งค่าทุกอย่างเพื่อให้สคริปต์บันทึกไฟล์ Excel พร้อมผลการค้นหาในโฟลเดอร์ Dropbox ซึ่งจะช่วยให้คุณดูไฟล์ดังกล่าวได้จากทุกที่และทุกเวลา

Python - ตัวช่วยในการค้นหาตั๋วเครื่องบินราคาถูกสำหรับผู้ที่รักการเดินทาง
ฉันยังไม่พบภาษีที่มีข้อผิดพลาด แต่ฉันคิดว่ามันเป็นไปได้

เมื่อค้นหาดังที่กล่าวไปแล้ว จะใช้ "วันที่ที่ยืดหยุ่น" สคริปต์ค้นหาข้อเสนอที่อยู่ภายในสามวันนับจากวันที่ที่กำหนด แม้ว่าเมื่อรันสคริปต์ มันจะค้นหาข้อเสนอในทิศทางเดียวเท่านั้น แต่ก็ง่ายต่อการแก้ไขเพื่อให้สามารถรวบรวมข้อมูลในเส้นทางการบินหลายเส้นทาง ด้วยความช่วยเหลือคุณสามารถค้นหาภาษีที่ผิดพลาดได้การค้นพบดังกล่าวน่าสนใจมาก

ทำไมคุณถึงต้องใช้เครื่องขูดเว็บอีก?

เมื่อฉันเริ่มต้นการขูดเว็บครั้งแรก จริงๆ แล้วฉันไม่ได้สนใจมันเป็นพิเศษ ฉันต้องการทำโครงการเพิ่มเติมในด้านการสร้างแบบจำลองการทำนาย การวิเคราะห์ทางการเงิน และอาจเป็นไปได้ในด้านการวิเคราะห์สีทางอารมณ์ของข้อความ แต่ปรากฎว่ามันน่าสนใจมากที่รู้วิธีสร้างโปรแกรมที่รวบรวมข้อมูลจากเว็บไซต์ ขณะที่ฉันเจาะลึกหัวข้อนี้ ฉันพบว่าการขูดเว็บเป็น "กลไก" ของอินเทอร์เน็ต

คุณอาจคิดว่านี่เป็นข้อความที่หนาเกินไป แต่ลองพิจารณาว่า Google เริ่มต้นด้วยเครื่องขูดเว็บที่ Larry Page สร้างขึ้นโดยใช้ Java และ Python โรบ็อตของ Google ได้สำรวจอินเทอร์เน็ตโดยพยายามให้คำตอบที่ดีที่สุดสำหรับคำถามแก่ผู้ใช้ Web scraping มีประโยชน์อย่างไม่มีที่สิ้นสุด และแม้ว่าคุณจะสนใจอย่างอื่นใน Data Science คุณก็ยังต้องมีทักษะในการดึงข้อมูลเพื่อที่จะวิเคราะห์ข้อมูล

ฉันพบว่ามีเทคนิคบางอย่างที่ใช้ที่นี่อย่างมหัศจรรย์ หนังสือ เกี่ยวกับการขูดเว็บซึ่งฉันเพิ่งได้มา ประกอบด้วยตัวอย่างและแนวคิดง่ายๆ มากมายสำหรับการประยุกต์ใช้สิ่งที่คุณได้เรียนรู้ในทางปฏิบัติ นอกจากนี้ยังมีบทที่น่าสนใจมากเกี่ยวกับการข้ามการตรวจสอบ reCaptcha นี่เป็นข่าวสำหรับฉัน เนื่องจากฉันไม่รู้ด้วยซ้ำว่ามีเครื่องมือพิเศษและแม้แต่บริการทั้งหมดสำหรับการแก้ปัญหาดังกล่าว

คุณชอบท่องเที่ยวไหม?!

สำหรับคำถามที่เรียบง่ายและไม่เป็นอันตรายซึ่งตั้งไว้ในหัวเรื่องของหัวข้อนี้ คุณมักจะได้ยินคำตอบเชิงบวก พร้อมด้วยเรื่องราวสองสามเรื่องจากการเดินทางของผู้ถูกถามด้วย พวกเราส่วนใหญ่คงเห็นพ้องกันว่าการเดินทางเป็นวิธีที่ดีในการดื่มด่ำกับสภาพแวดล้อมทางวัฒนธรรมใหม่ๆ และเปิดโลกทัศน์ของคุณให้กว้างขึ้น อย่างไรก็ตาม หากคุณถามใครสักคนว่าพวกเขาชอบค้นหาตั๋วเครื่องบินหรือไม่ ฉันแน่ใจว่าคำตอบจะไม่เป็นเชิงบวกมากนัก ตามความเป็นจริงแล้ว Python มาช่วยเหลือเราที่นี่

ภารกิจแรกที่เราต้องแก้ไขในการสร้างระบบค้นหาข้อมูลตั๋วเครื่องบินคือการเลือกแพลตฟอร์มที่เหมาะสมซึ่งเราจะรับข้อมูล การแก้ปัญหานี้ไม่ใช่เรื่องง่ายสำหรับฉัน แต่สุดท้ายฉันก็เลือกบริการพายเรือคายัค ฉันลองใช้บริการของ Momondo, Skyscanner, Expedia และอื่นๆ สองสามบริการ แต่กลไกการป้องกันหุ่นยนต์ในทรัพยากรเหล่านี้ไม่สามารถเข้าถึงได้ หลังจากพยายามหลายครั้ง ในระหว่างนั้นฉันต้องจัดการกับสัญญาณไฟจราจร ทางม้าลาย และจักรยาน พยายามโน้มน้าวระบบต่างๆ ว่าฉันเป็นมนุษย์ ฉันตัดสินใจว่าเรือคายัคเหมาะที่สุดสำหรับฉัน แม้ว่าจะมีการโหลดหน้าเว็บมากเกินไปก็ตาม ในเวลาอันสั้น และเริ่มการตรวจสอบด้วย ฉันจัดการเพื่อให้บอทส่งคำขอไปยังไซต์ในช่วงเวลา 4 ถึง 6 ชั่วโมงและทุกอย่างทำงานได้ดี ในบางครั้ง ความยากลำบากเกิดขึ้นเมื่อทำงานกับเรือคายัค แต่ถ้าพวกเขาเริ่มรบกวนคุณด้วยเช็ค คุณจะต้องจัดการกับพวกเขาด้วยตนเองแล้วจึงเปิดบอท หรือรอสองสามชั่วโมงแล้วการตรวจสอบควรจะหยุด หากจำเป็น คุณสามารถปรับเปลี่ยนโค้ดสำหรับแพลตฟอร์มอื่นได้อย่างง่ายดาย และหากเป็นเช่นนั้น คุณสามารถรายงานได้ในความคิดเห็น

หากคุณเพิ่งเริ่มต้นใช้งานการขูดเว็บและไม่รู้ว่าทำไมบางเว็บไซต์ถึงประสบปัญหานี้ ก่อนที่คุณจะเริ่มโครงการแรกของคุณในด้านนี้ ให้ช่วยเหลือตัวเองและค้นหาคำว่า "มารยาทในการขูดเว็บ" ใน Google . การทดลองของคุณอาจสิ้นสุดเร็วกว่าที่คุณคิดหากคุณทำการขูดเว็บโดยไม่ฉลาด

เริ่มต้นใช้งาน

ต่อไปนี้เป็นภาพรวมทั่วไปของสิ่งที่จะเกิดขึ้นในโค้ดขูดเว็บของเรา:

  • นำเข้าไลบรารีที่จำเป็น
  • การเปิดแท็บ Google Chrome
  • เรียกใช้ฟังก์ชันที่เริ่มบอทโดยส่งเมืองและวันที่ที่จะใช้ในการค้นหาตั๋ว
  • ฟังก์ชันนี้ใช้ผลการค้นหาแรก จัดเรียงตามดีที่สุด และคลิกปุ่มเพื่อโหลดผลลัพธ์เพิ่มเติม
  • ฟังก์ชั่นอื่นรวบรวมข้อมูลจากทั้งหน้าและส่งกลับกรอบข้อมูล
  • สองขั้นตอนก่อนหน้านี้ดำเนินการโดยใช้ประเภทการเรียงลำดับตามราคาตั๋ว (ถูก) และตามความเร็วเที่ยวบิน (เร็วที่สุด)
  • ผู้ใช้สคริปต์จะได้รับอีเมลที่มีการสรุปราคาตั๋ว (ตั๋วที่ถูกที่สุดและราคาเฉลี่ย) และกรอบข้อมูลที่มีข้อมูลที่จัดเรียงตามตัวบ่งชี้ทั้งสามที่กล่าวถึงข้างต้นจะถูกบันทึกเป็นไฟล์ Excel
  • การดำเนินการข้างต้นทั้งหมดจะดำเนินการในรอบหลังจากระยะเวลาที่กำหนด

ควรสังเกตว่าทุกโครงการ Selenium เริ่มต้นด้วยไดรเวอร์เว็บ ฉันใช้ ไดรเวอร์ Chromeฉันทำงานกับ Google Chrome แต่มีตัวเลือกอื่น PhantomJS และ Firefox ก็ได้รับความนิยมเช่นกัน หลังจากดาวน์โหลดไดรเวอร์แล้ว คุณจะต้องวางไว้ในโฟลเดอร์ที่เหมาะสม และเสร็จสิ้นการเตรียมการใช้งาน บรรทัดแรกของสคริปต์ของเราจะเปิดแท็บ Chrome ใหม่

โปรดทราบว่าในเรื่องราวของฉัน ฉันไม่ได้พยายามเปิดโลกทัศน์ใหม่ๆ ในการค้นหาตั๋วเครื่องบินราคาสุดคุ้ม มีวิธีการค้นหาข้อเสนอดังกล่าวขั้นสูงกว่ามาก ฉันแค่อยากเสนอวิธีการง่ายๆ แต่ใช้งานได้จริงแก่ผู้อ่านเนื้อหานี้ในการแก้ปัญหานี้

นี่คือรหัสที่เราพูดถึงข้างต้น

from time import sleep, strftime
from random import randint
import pandas as pd
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import smtplib
from email.mime.multipart import MIMEMultipart

# Используйте тут ваш путь к chromedriver!
chromedriver_path = 'C:/{YOUR PATH HERE}/chromedriver_win32/chromedriver.exe'

driver = webdriver.Chrome(executable_path=chromedriver_path) # Этой командой открывается окно Chrome
sleep(2)

ที่ตอนต้นของโค้ด คุณจะเห็นคำสั่งนำเข้าแพ็คเกจที่ใช้ตลอดโปรเจ็กต์ของเรา ดังนั้น, randint ใช้เพื่อทำให้บอท “หลับ” เป็นเวลาสุ่มจำนวนวินาทีก่อนที่จะเริ่มการดำเนินการค้นหาใหม่ โดยปกติแล้ว ไม่มีบอทตัวเดียวที่สามารถทำได้หากไม่มีสิ่งนี้ หากคุณเรียกใช้โค้ดด้านบน หน้าต่าง Chrome จะเปิดขึ้น ซึ่งบอทจะใช้ทำงานกับไซต์ต่างๆ

มาทำการทดลองเล็กๆ น้อยๆ แล้วเปิดเว็บไซต์ Kayak.com ในหน้าต่างแยกต่างหาก เราจะเลือกเมืองที่เราจะบิน และเมืองที่เราต้องการไป รวมถึงวันที่ของเที่ยวบิน เมื่อเลือกวันที่ ตรวจสอบให้แน่ใจว่าใช้ช่วง +-3 วัน ฉันเขียนโค้ดโดยคำนึงถึงสิ่งที่ไซต์สร้างขึ้นเพื่อตอบสนองต่อคำขอดังกล่าว ตัวอย่างเช่น หากคุณต้องการค้นหาตั๋วเฉพาะวันที่ระบุ มีความเป็นไปได้สูงที่คุณจะต้องแก้ไขโค้ดบอท เมื่อฉันพูดถึงโค้ด ฉันจะให้คำอธิบายที่เหมาะสม แต่หากคุณรู้สึกสับสน โปรดแจ้งให้เราทราบ

ตอนนี้คลิกที่ปุ่มค้นหาแล้วดูลิงค์ในแถบที่อยู่ ควรคล้ายกับลิงก์ที่ฉันใช้ในตัวอย่างด้านล่างซึ่งมีการประกาศตัวแปร kayakซึ่งเก็บ URL และวิธีการใช้งาน get ไดรเวอร์เว็บ หลังจากคลิกปุ่มค้นหาแล้ว ผลลัพธ์ควรปรากฏบนหน้า

Python - ตัวช่วยในการค้นหาตั๋วเครื่องบินราคาถูกสำหรับผู้ที่รักการเดินทาง
เมื่อผมใช้คำสั่ง get มากกว่าสองหรือสามครั้งภายในไม่กี่นาที ฉันถูกขอให้ทำการยืนยันให้เสร็จสิ้นโดยใช้ reCaptcha คุณสามารถผ่านการตรวจสอบนี้ด้วยตนเองและทำการทดลองต่อไปจนกว่าระบบจะตัดสินใจดำเนินการตรวจสอบใหม่ เมื่อฉันทดสอบสคริปต์ ดูเหมือนว่าเซสชันการค้นหาแรกจะดำเนินไปอย่างราบรื่นเสมอ ดังนั้น หากคุณต้องการทดสอบโค้ด คุณจะต้องตรวจสอบด้วยตนเองเป็นระยะ ๆ และปล่อยให้โค้ดทำงาน โดยใช้ช่วงเวลาที่ยาวนานระหว่างเซสชันการค้นหา และหากคุณลองคิดดู บุคคลนั้นไม่น่าจะต้องการข้อมูลเกี่ยวกับราคาตั๋วที่ได้รับในช่วงเวลา 10 นาทีระหว่างการค้นหา

การทำงานกับเพจโดยใช้ XPath

ดังนั้นเราจึงเปิดหน้าต่างและโหลดไซต์ หากต้องการรับราคาและข้อมูลอื่นๆ เราจำเป็นต้องใช้เทคโนโลยี XPath หรือตัวเลือก CSS ฉันตัดสินใจใช้ XPath ต่อไปและไม่รู้สึกว่าจำเป็นต้องใช้ตัวเลือก CSS แต่ก็ค่อนข้างเป็นไปได้ที่จะทำงานในลักษณะนั้น การนำทางไปรอบๆ หน้าโดยใช้ XPath อาจยุ่งยาก และแม้ว่าคุณจะใช้เทคนิคที่ฉันอธิบายไว้ก็ตาม นี้ บทความที่เกี่ยวข้องกับการคัดลอกตัวระบุที่เกี่ยวข้องจากโค้ดหน้า ฉันรู้ว่านี่ไม่ใช่วิธีที่ดีที่สุดในการเข้าถึงองค์ประกอบที่จำเป็น โดยวิธีการใน นี้ หนังสือเล่มนี้ให้คำอธิบายที่ดีเยี่ยมเกี่ยวกับพื้นฐานการทำงานกับเพจต่างๆ โดยใช้ตัวเลือก XPath และ CSS นี่คือลักษณะของวิธีการควบคุมเว็บที่เกี่ยวข้อง

Python - ตัวช่วยในการค้นหาตั๋วเครื่องบินราคาถูกสำหรับผู้ที่รักการเดินทาง
ดังนั้นเรามาทำงานกับบอทต่อไป ลองใช้ความสามารถของโปรแกรมเพื่อเลือกตั๋วที่ถูกที่สุด ในภาพต่อไปนี้ รหัสตัวเลือก XPath จะถูกเน้นด้วยสีแดง หากต้องการดูโค้ด คุณต้องคลิกขวาที่องค์ประกอบของหน้าที่คุณสนใจ และเลือกคำสั่งตรวจสอบจากเมนูที่ปรากฏขึ้น คำสั่งนี้สามารถเรียกใช้สำหรับองค์ประกอบของหน้าต่างๆ ซึ่งโค้ดจะแสดงและเน้นในตัวแสดงโค้ด

Python - ตัวช่วยในการค้นหาตั๋วเครื่องบินราคาถูกสำหรับผู้ที่รักการเดินทาง
ดูรหัสหน้า

เพื่อที่จะค้นหาการยืนยันเหตุผลของฉันเกี่ยวกับข้อเสียของการคัดลอกตัวเลือกจากโค้ด โปรดใส่ใจกับคุณสมบัติต่อไปนี้

นี่คือสิ่งที่คุณจะได้รับเมื่อคุณคัดลอกโค้ด:

//*[@id="wtKI-price_aTab"]/div[1]/div/div/div[1]/div/span/span

ในการคัดลอกสิ่งนี้ คุณต้องคลิกขวาที่ส่วนของโค้ดที่คุณสนใจ และเลือกคำสั่ง Copy > Copy XPath จากเมนูที่ปรากฏขึ้น

นี่คือสิ่งที่ฉันใช้ในการกำหนดปุ่มที่ถูกที่สุด:

cheap_results = ‘//a[@data-code = "price"]’

Python - ตัวช่วยในการค้นหาตั๋วเครื่องบินราคาถูกสำหรับผู้ที่รักการเดินทาง
คัดลอกคำสั่ง > คัดลอก XPath

เห็นได้ชัดว่าตัวเลือกที่สองดูง่ายกว่ามาก เมื่อใช้ จะค้นหาองค์ประกอบ a ที่มีแอตทริบิวต์ data-code, เท่ากัน price. เมื่อใช้ตัวเลือกแรก องค์ประกอบจะถูกค้นหา id ซึ่งเท่ากับ wtKI-price_aTabและเส้นทาง XPath ไปยังองค์ประกอบมีลักษณะเช่นนี้ /div[1]/div/div/div[1]/div/span/span. แบบสอบถาม XPath เช่นนี้กับเพจจะช่วยได้ แต่เพียงครั้งเดียวเท่านั้น ฉันสามารถพูดได้ในขณะนี้ว่า id จะเปลี่ยนในครั้งถัดไปที่โหลดเพจ ลำดับอักขระ wtKI เปลี่ยนแปลงแบบไดนามิกทุกครั้งที่โหลดเพจ ดังนั้นโค้ดที่ใช้จะไม่มีประโยชน์หลังจากการโหลดหน้าถัดไป ดังนั้นใช้เวลาทำความเข้าใจ XPath บ้าง ความรู้นี้จะให้บริการคุณได้เป็นอย่างดี

อย่างไรก็ตาม ควรสังเกตว่าการคัดลอกตัวเลือก XPath จะมีประโยชน์เมื่อทำงานกับไซต์ที่ค่อนข้างเรียบง่าย และหากคุณพอใจกับสิ่งนี้ ก็ไม่มีอะไรผิดปกติ

ทีนี้ลองมาคิดว่าจะทำอย่างไรถ้าคุณต้องการให้ผลการค้นหาทั้งหมดอยู่ในหลายบรรทัดภายในรายการ ง่ายมาก. ผลลัพธ์แต่ละรายการจะอยู่ภายในวัตถุที่มีคลาส resultWrapper. การโหลดผลลัพธ์ทั้งหมดสามารถทำได้ในวงวนที่คล้ายกับที่แสดงด้านล่าง

ควรสังเกตว่าหากคุณเข้าใจสิ่งที่กล่าวมาข้างต้น คุณควรเข้าใจโค้ดส่วนใหญ่ที่เราจะวิเคราะห์ได้อย่างง่ายดาย ขณะที่โค้ดนี้ทำงาน เราจะเข้าถึงสิ่งที่เราต้องการ (อันที่จริงคือองค์ประกอบที่ห่อผลลัพธ์ไว้) โดยใช้กลไกการระบุพาธ (XPath) สิ่งนี้ทำเพื่อรับข้อความขององค์ประกอบและวางไว้ในวัตถุที่สามารถอ่านข้อมูลได้ (ใช้ครั้งแรก flight_containers, แล้ว - flights_list).

Python - ตัวช่วยในการค้นหาตั๋วเครื่องบินราคาถูกสำหรับผู้ที่รักการเดินทาง
สามบรรทัดแรกปรากฏขึ้นและเราสามารถมองเห็นทุกสิ่งที่เราต้องการได้อย่างชัดเจน อย่างไรก็ตาม เรามีวิธีรับข้อมูลที่น่าสนใจมากกว่านี้ เราจำเป็นต้องนำข้อมูลจากแต่ละองค์ประกอบแยกกัน

ไปทำงาน!

วิธีที่ง่ายที่สุดในการเขียนฟังก์ชันคือการโหลดผลลัพธ์เพิ่มเติม นั่นคือสิ่งที่เราจะเริ่มต้น ฉันต้องการเพิ่มจำนวนเที่ยวบินที่โปรแกรมได้รับข้อมูลโดยไม่ทำให้เกิดความสงสัยในบริการที่นำไปสู่การตรวจสอบ ฉันจึงคลิกปุ่มโหลดผลลัพธ์เพิ่มเติมทุกครั้งที่แสดงหน้าเว็บ ในโค้ดนี้ คุณควรให้ความสนใจกับบล็อก tryซึ่งฉันเพิ่มเข้าไปเพราะบางครั้งปุ่มโหลดไม่ถูกต้อง หากคุณพบสิ่งนี้เช่นกัน ให้ใส่เครื่องหมายความคิดเห็นการเรียกใช้ฟังก์ชันนี้ในโค้ดฟังก์ชัน start_kayakซึ่งเราจะดูด้านล่าง

# Загрузка большего количества результатов для того, чтобы максимизировать объём собираемых данных
def load_more():
    try:
        more_results = '//a[@class = "moreButton"]'
        driver.find_element_by_xpath(more_results).click()
        # Вывод этих заметок в ходе работы программы помогает мне быстро выяснить то, чем она занята
        print('sleeping.....')
        sleep(randint(45,60))
    except:
        pass

ตอนนี้ หลังจากการวิเคราะห์ฟังก์ชันนี้มาอย่างยาวนาน (บางครั้งฉันก็ถูกมองข้ามไป) เราก็พร้อมที่จะประกาศฟังก์ชันที่จะขูดหน้า

ฉันได้รวบรวมสิ่งที่จำเป็นส่วนใหญ่ในฟังก์ชันต่อไปนี้ที่เรียกว่าแล้ว page_scrape. บางครั้งข้อมูลเส้นทางที่ส่งคืนจะถูกรวมเข้าด้วยกัน ดังนั้นฉันจึงใช้วิธีง่ายๆ เพื่อแยกข้อมูลดังกล่าว เช่น เมื่อฉันใช้ตัวแปรเป็นครั้งแรก section_a_list и section_b_list. ฟังก์ชั่นของเราส่งคืนกรอบข้อมูล flights_dfซึ่งช่วยให้เราสามารถแยกผลลัพธ์ที่ได้จากวิธีการจัดเรียงข้อมูลต่างๆ แล้วรวมเข้าด้วยกันในภายหลัง

def page_scrape():
    """This function takes care of the scraping part"""
    
    xp_sections = '//*[@class="section duration"]'
    sections = driver.find_elements_by_xpath(xp_sections)
    sections_list = [value.text for value in sections]
    section_a_list = sections_list[::2] # так мы разделяем информацию о двух полётах
    section_b_list = sections_list[1::2]
    
    # Если вы наткнулись на reCaptcha, вам может понадобиться что-то предпринять.
    # О том, что что-то пошло не так, вы узнаете исходя из того, что вышеприведённые списки пусты
    # это выражение if позволяет завершить работу программы или сделать ещё что-нибудь
    # тут можно приостановить работу, что позволит вам пройти проверку и продолжить скрапинг
    # я использую тут SystemExit так как хочу протестировать всё с самого начала
    if section_a_list == []:
        raise SystemExit
    
    # Я буду использовать букву A для уходящих рейсов и B для прибывающих
    a_duration = []
    a_section_names = []
    for n in section_a_list:
        # Получаем время
        a_section_names.append(''.join(n.split()[2:5]))
        a_duration.append(''.join(n.split()[0:2]))
    b_duration = []
    b_section_names = []
    for n in section_b_list:
        # Получаем время
        b_section_names.append(''.join(n.split()[2:5]))
        b_duration.append(''.join(n.split()[0:2]))

    xp_dates = '//div[@class="section date"]'
    dates = driver.find_elements_by_xpath(xp_dates)
    dates_list = [value.text for value in dates]
    a_date_list = dates_list[::2]
    b_date_list = dates_list[1::2]
    # Получаем день недели
    a_day = [value.split()[0] for value in a_date_list]
    a_weekday = [value.split()[1] for value in a_date_list]
    b_day = [value.split()[0] for value in b_date_list]
    b_weekday = [value.split()[1] for value in b_date_list]
    
    # Получаем цены
    xp_prices = '//a[@class="booking-link"]/span[@class="price option-text"]'
    prices = driver.find_elements_by_xpath(xp_prices)
    prices_list = [price.text.replace('$','') for price in prices if price.text != '']
    prices_list = list(map(int, prices_list))

    # stops - это большой список, в котором первый фрагмент пути находится по чётному индексу, а второй - по нечётному
    xp_stops = '//div[@class="section stops"]/div[1]'
    stops = driver.find_elements_by_xpath(xp_stops)
    stops_list = [stop.text[0].replace('n','0') for stop in stops]
    a_stop_list = stops_list[::2]
    b_stop_list = stops_list[1::2]

    xp_stops_cities = '//div[@class="section stops"]/div[2]'
    stops_cities = driver.find_elements_by_xpath(xp_stops_cities)
    stops_cities_list = [stop.text for stop in stops_cities]
    a_stop_name_list = stops_cities_list[::2]
    b_stop_name_list = stops_cities_list[1::2]
    
    # сведения о компании-перевозчике, время отправления и прибытия для обоих рейсов
    xp_schedule = '//div[@class="section times"]'
    schedules = driver.find_elements_by_xpath(xp_schedule)
    hours_list = []
    carrier_list = []
    for schedule in schedules:
        hours_list.append(schedule.text.split('n')[0])
        carrier_list.append(schedule.text.split('n')[1])
    # разделяем сведения о времени и о перевозчиках между рейсами a и b
    a_hours = hours_list[::2]
    a_carrier = carrier_list[1::2]
    b_hours = hours_list[::2]
    b_carrier = carrier_list[1::2]

    
    cols = (['Out Day', 'Out Time', 'Out Weekday', 'Out Airline', 'Out Cities', 'Out Duration', 'Out Stops', 'Out Stop Cities',
            'Return Day', 'Return Time', 'Return Weekday', 'Return Airline', 'Return Cities', 'Return Duration', 'Return Stops', 'Return Stop Cities',
            'Price'])

    flights_df = pd.DataFrame({'Out Day': a_day,
                               'Out Weekday': a_weekday,
                               'Out Duration': a_duration,
                               'Out Cities': a_section_names,
                               'Return Day': b_day,
                               'Return Weekday': b_weekday,
                               'Return Duration': b_duration,
                               'Return Cities': b_section_names,
                               'Out Stops': a_stop_list,
                               'Out Stop Cities': a_stop_name_list,
                               'Return Stops': b_stop_list,
                               'Return Stop Cities': b_stop_name_list,
                               'Out Time': a_hours,
                               'Out Airline': a_carrier,
                               'Return Time': b_hours,
                               'Return Airline': b_carrier,                           
                               'Price': prices_list})[cols]
    
    flights_df['timestamp'] = strftime("%Y%m%d-%H%M") # время сбора данных
    return flights_df

ฉันพยายามตั้งชื่อตัวแปรเพื่อให้โค้ดเข้าใจได้ จำไว้ว่าตัวแปรที่เริ่มต้นด้วย a อยู่ในขั้นแรกของเส้นทางและ b - ถึงวินาที มาดูฟังก์ชั่นถัดไปกันดีกว่า

กลไกสนับสนุน

ขณะนี้เรามีฟังก์ชันที่ช่วยให้เราสามารถโหลดผลการค้นหาเพิ่มเติมและฟังก์ชันในการประมวลผลผลลัพธ์เหล่านั้นได้ บทความนี้อาจจบลงที่นี่ เนื่องจากฟังก์ชันทั้งสองนี้มีทุกสิ่งที่คุณต้องการในการคัดลอกหน้าเว็บที่คุณสามารถเปิดได้ด้วยตัวเอง แต่เรายังไม่ได้พิจารณากลไกเสริมบางอย่างที่กล่าวถึงข้างต้น เช่น นี่คือโค้ดสำหรับส่งอีเมลและอื่นๆ ทั้งหมดนี้สามารถพบได้ในฟังก์ชั่น start_kayakซึ่งตอนนี้เราจะพิจารณา

เพื่อให้ฟังก์ชันนี้ใช้งานได้ คุณต้องมีข้อมูลเกี่ยวกับเมืองและวันที่ การใช้ข้อมูลนี้จะสร้างลิงก์ในตัวแปร kayakซึ่งใช้เพื่อนำคุณไปยังหน้าเว็บที่จะมีผลการค้นหาที่จัดเรียงตามรายการที่ตรงกับข้อความค้นหามากที่สุด หลังจากเซสชันขูดครั้งแรก เราจะดำเนินการกับราคาในตารางที่ด้านบนของหน้า คือเราจะหาราคาตั๋วขั้นต่ำและราคาเฉลี่ย ทั้งหมดนี้พร้อมกับคำทำนายที่ออกโดยเว็บไซต์จะถูกส่งทางอีเมล บนหน้า ตารางที่เกี่ยวข้องควรอยู่ที่มุมซ้ายบน การทำงานกับตารางนี้อาจทำให้เกิดข้อผิดพลาดเมื่อค้นหาโดยใช้วันที่ที่แน่นอนเนื่องจากในกรณีนี้ตารางจะไม่แสดงบนหน้า

def start_kayak(city_from, city_to, date_start, date_end):
    """City codes - it's the IATA codes!
    Date format -  YYYY-MM-DD"""
    
    kayak = ('https://www.kayak.com/flights/' + city_from + '-' + city_to +
             '/' + date_start + '-flexible/' + date_end + '-flexible?sort=bestflight_a')
    driver.get(kayak)
    sleep(randint(8,10))
    
    # иногда появляется всплывающее окно, для проверки на это и его закрытия можно воспользоваться блоком try
    try:
        xp_popup_close = '//button[contains(@id,"dialog-close") and contains(@class,"Button-No-Standard-Style close ")]'
        driver.find_elements_by_xpath(xp_popup_close)[5].click()
    except Exception as e:
        pass
    sleep(randint(60,95))
    print('loading more.....')
    
#     load_more()
    
    print('starting first scrape.....')
    df_flights_best = page_scrape()
    df_flights_best['sort'] = 'best'
    sleep(randint(60,80))
    
    # Возьмём самую низкую цену из таблицы, расположенной в верхней части страницы
    matrix = driver.find_elements_by_xpath('//*[contains(@id,"FlexMatrixCell")]')
    matrix_prices = [price.text.replace('$','') for price in matrix]
    matrix_prices = list(map(int, matrix_prices))
    matrix_min = min(matrix_prices)
    matrix_avg = sum(matrix_prices)/len(matrix_prices)
    
    print('switching to cheapest results.....')
    cheap_results = '//a[@data-code = "price"]'
    driver.find_element_by_xpath(cheap_results).click()
    sleep(randint(60,90))
    print('loading more.....')
    
#     load_more()
    
    print('starting second scrape.....')
    df_flights_cheap = page_scrape()
    df_flights_cheap['sort'] = 'cheap'
    sleep(randint(60,80))
    
    print('switching to quickest results.....')
    quick_results = '//a[@data-code = "duration"]'
    driver.find_element_by_xpath(quick_results).click()  
    sleep(randint(60,90))
    print('loading more.....')
    
#     load_more()
    
    print('starting third scrape.....')
    df_flights_fast = page_scrape()
    df_flights_fast['sort'] = 'fast'
    sleep(randint(60,80))
    
    # Сохранение нового фрейма в Excel-файл, имя которого отражает города и даты
    final_df = df_flights_cheap.append(df_flights_best).append(df_flights_fast)
    final_df.to_excel('search_backups//{}_flights_{}-{}_from_{}_to_{}.xlsx'.format(strftime("%Y%m%d-%H%M"),
                                                                                   city_from, city_to, 
                                                                                   date_start, date_end), index=False)
    print('saved df.....')
    
    # Можно следить за тем, как прогноз, выдаваемый сайтом, соотносится с реальностью
    xp_loading = '//div[contains(@id,"advice")]'
    loading = driver.find_element_by_xpath(xp_loading).text
    xp_prediction = '//span[@class="info-text"]'
    prediction = driver.find_element_by_xpath(xp_prediction).text
    print(loading+'n'+prediction)
    
    # иногда в переменной loading оказывается эта строка, которая, позже, вызывает проблемы с отправкой письма
    # если это прозошло - меняем её на "Not Sure"
    weird = '¯_(ツ)_/¯'
    if loading == weird:
        loading = 'Not sure'
    
    username = '[email protected]'
    password = 'YOUR PASSWORD'

    server = smtplib.SMTP('smtp.outlook.com', 587)
    server.ehlo()
    server.starttls()
    server.login(username, password)
    msg = ('Subject: Flight Scrapernn
Cheapest Flight: {}nAverage Price: {}nnRecommendation: {}nnEnd of message'.format(matrix_min, matrix_avg, (loading+'n'+prediction)))
    message = MIMEMultipart()
    message['From'] = '[email protected]'
    message['to'] = '[email protected]'
    server.sendmail('[email protected]', '[email protected]', msg)
    print('sent email.....')

ฉันทดสอบสคริปต์นี้โดยใช้บัญชี Outlook (hotmail.com) ฉันยังไม่ได้ทดสอบว่ามันทำงานอย่างถูกต้องกับบัญชี Gmail ระบบอีเมลนี้ค่อนข้างได้รับความนิยม แต่มีตัวเลือกที่เป็นไปได้มากมาย หากคุณใช้บัญชี Hotmail เพื่อให้ทุกอย่างทำงานได้คุณเพียงแค่ต้องป้อนข้อมูลของคุณลงในรหัส

หากคุณต้องการทำความเข้าใจอย่างชัดเจนว่ากำลังทำอะไรในส่วนเฉพาะของโค้ดสำหรับฟังก์ชันนี้ คุณสามารถคัดลอกและทดลองกับพวกมันได้ การทดลองโค้ดเป็นวิธีเดียวที่จะเข้าใจโค้ดได้อย่างแท้จริง

ระบบพร้อม.

ตอนนี้เราได้ทำทุกอย่างที่เราพูดถึงแล้ว เราสามารถสร้างวงง่ายๆ ที่เรียกใช้ฟังก์ชันของเราได้ สคริปต์ขอข้อมูลจากผู้ใช้เกี่ยวกับเมืองและวันที่ เมื่อทดสอบโดยรีสตาร์ทสคริปต์อย่างต่อเนื่อง คุณไม่น่าจะต้องการป้อนข้อมูลนี้ด้วยตนเองทุกครั้ง ดังนั้นบรรทัดที่เกี่ยวข้องในช่วงเวลาของการทดสอบสามารถใส่ความคิดเห็นออกได้โดยไม่ใส่เครื่องหมายข้อคิดเห็นที่ด้านล่าง ซึ่งเป็นข้อมูลที่ต้องการโดย สคริปต์เป็นแบบฮาร์ดโค้ด

city_from = input('From which city? ')
city_to = input('Where to? ')
date_start = input('Search around which departure date? Please use YYYY-MM-DD format only ')
date_end = input('Return when? Please use YYYY-MM-DD format only ')

# city_from = 'LIS'
# city_to = 'SIN'
# date_start = '2019-08-21'
# date_end = '2019-09-07'

for n in range(0,5):
    start_kayak(city_from, city_to, date_start, date_end)
    print('iteration {} was complete @ {}'.format(n, strftime("%Y%m%d-%H%M")))
    
    # Ждём 4 часа
    sleep(60*60*4)
    print('sleep finished.....')

นี่คือลักษณะของการทดสอบการทำงานสคริปต์
Python - ตัวช่วยในการค้นหาตั๋วเครื่องบินราคาถูกสำหรับผู้ที่รักการเดินทาง
ทดสอบการทำงานของสคริปต์

ผลของการ

หากคุณมาไกลขนาดนี้ ยินดีด้วย! ตอนนี้คุณมีเครื่องมือขูดเว็บที่ใช้งานได้แล้ว แม้ว่าฉันจะเห็นวิธีปรับปรุงได้หลายวิธีก็ตาม ตัวอย่างเช่น สามารถรวมเข้ากับ Twilio เพื่อให้สามารถส่งข้อความแทนอีเมลได้ คุณสามารถใช้ VPN หรืออย่างอื่นเพื่อรับผลลัพธ์จากเซิร์ฟเวอร์หลายเครื่องพร้อมกันได้ นอกจากนี้ยังมีปัญหาเกิดขึ้นเป็นระยะกับการตรวจสอบผู้ใช้เว็บไซต์เพื่อดูว่าเขาเป็นคนหรือไม่ แต่ปัญหานี้ก็สามารถแก้ไขได้เช่นกัน ไม่ว่าในกรณีใด ตอนนี้คุณมีฐานที่คุณสามารถขยายได้หากต้องการ ตัวอย่างเช่น ตรวจสอบให้แน่ใจว่าไฟล์ Excel ถูกส่งไปยังผู้ใช้เป็นไฟล์แนบในอีเมล

Python - ตัวช่วยในการค้นหาตั๋วเครื่องบินราคาถูกสำหรับผู้ที่รักการเดินทาง

เฉพาะผู้ใช้ที่ลงทะเบียนเท่านั้นที่สามารถเข้าร่วมในการสำรวจได้ เข้าสู่ระบบ, โปรด.

คุณใช้เทคโนโลยีการขูดเว็บหรือไม่?

  • มี

  • ไม่

ผู้ใช้ 8 คนโหวต ผู้ใช้ 1 รายงดออกเสียง

ที่มา: will.com

เพิ่มความคิดเห็น