chrome网页视频抓取(爬虫北京二手车数据的过程及应用方法(一))
优采云 发布时间: 2021-10-04 07:29chrome网页视频抓取(爬虫北京二手车数据的过程及应用方法(一))
基于golang的爬虫实战前言
爬虫本来就是蟒蛇的强项。早期学习scrapy,写了一些简单的爬虫小程序。但是后来突然对golang产生了兴趣,决定写爬虫来练手。由于我是golang新手,有错误请指正。
下载依赖包需要的库gopkg.in/gomail.v2缺点的大致思路
go get github.com/tebeka/selenium
go get gopkg.in/gomail.v2
代码
// StartChrome 启动谷歌浏览器headless模式
func StartChrome() {
opts := []selenium.ServiceOption{}
caps := selenium.Capabilities{
"browserName": "chrome",
}
// 禁止加载图片,加快渲染速度
imagCaps := map[string]interface{}{
"profile.managed_default_content_settings.images": 2,
}
chromeCaps := chrome.Capabilities{
Prefs: imagCaps,
Path: "",
Args: []string{
"--headless", // 设置Chrome无头模式
"--no-sandbox",
"--user-agent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_2) AppleWebKit/604.4.7 (KHTML, like Gecko) Version/11.0.2 Safari/604.4.7", // 模拟user-agent,防反爬
},
}
caps.AddChrome(chromeCaps)
// 启动chromedriver,端口号可自定义
service, err = selenium.NewChromeDriverService("/opt/google/chrome/chromedriver", 9515, opts...)
if err != nil {
log.Printf("Error starting the ChromeDriver server: %v", err)
}
// 调起chrome浏览器
webDriver, err = selenium.NewRemote(caps, fmt.Sprintf("http://localhost:%d/wd/hub", 9515))
if err != nil {
panic(err)
}
// 这是目标网站留下的坑,不加这个在linux系统中会显示手机网页,每个网站的策略不一样,需要区别处理。
webDriver.AddCookie(&selenium.Cookie{
Name: "defaultJumpDomain",
Value: "www",
})
// 导航到目标网站
err = webDriver.Get(urlBeijing)
if err != nil {
panic(fmt.Sprintf("Failed to load page: %s\n", err))
}
log.Println(webDriver.Title())
}
通过上面的代码,可以通过代码启动Chrome,跳转到目标网站,方便下一步的数据采集。
// SetupWriter 初始化CSV
func SetupWriter() {
dateTime = time.Now().Format("2006-01-02 15:04:05") // 格式字符串是固定的,据说是go语言诞生时间,谷歌的恶趣味...
os.Mkdir("data", os.ModePerm)
csvFile, err := os.Create(fmt.Sprintf("data/%s.csv", dateTime))
if err != nil {
panic(err)
}
csvFile.WriteString("\xEF\xBB\xBF")
writer = csv.NewWriter(csvFile)
writer.Write([]string{"车型", "行驶里程", "首次上牌", "价格", "所在地", "门店"})
}
数据抓取
这部分是核心业务。每个网站都有不同的爬取方式,但是思路是一样的。通过xpath、css选择器、className、tagName等获取元素的内容,selenium的api可以实现大部分操作功能可以通过selenium源码看到。核心 API 包括 WebDriver 和 WebElement。下面是从北京二手车之家抓取二手车数据的过程。其他网站请参考修改过程。
const urlBeijing = "https://www.che168.com/beijing/list/#pvareaid=104646"
鼠标指向这句话右键,依次复制-XPath,就可以得到改变元素所在的xpath属性
//*[@id="viewlist_ul"]
然后通过代码
listContainer, err := webDriver.FindElement(selenium.ByXPATH, "//*[@id=\"viewlist_ul\"]")
可以获取html修改部分的WebElement对象。不难看出,这是所有数据的父容器。为了获取具体的数据,需要定位到每个元素子集,通过开发模式可以看出。
可以通过开发者工具获取类为carinfo,因为有多个元素,所以通过
lists, err := listContainer.FindElements(selenium.ByClassName, "carinfo")
您可以获得所有元素子集的集合。获取每个子集中的元素数据,需要遍历集合
for i := 0; i < len(lists); i++ {
var urlElem selenium.WebElement
if pageIndex == 1 {
urlElem, err = webDriver.FindElement(selenium.ByXPATH, fmt.Sprintf("//*[@id='viewlist_ul']/li[%d]/a", i+13))
} else {
urlElem, err = webDriver.FindElement(selenium.ByXPATH, fmt.Sprintf("//*[@id='viewlist_ul']/li[%d]/a", i+1))
}
if err != nil {
break
}
// 因为有些数据在次级页面,需要跳转
url, err := urlElem.GetAttribute("href")
if err != nil {
break
}
webDriver.Get(url)
title, _ := webDriver.Title()
log.Printf("当前页面标题:%s\n", title)
// 获取车辆型号
modelElem, err := webDriver.FindElement(selenium.ByXPATH, "/html/body/div[5]/div[2]/div[1]/h2")
var model string
if err != nil {
log.Println(err)
model = "暂无"
} else {
model, _ = modelElem.Text()
}
log.Printf("model=[%s]\n", model)
...
// 数据写入CSV
writer.Write([]string{model, miles, date, price, position, store})
writer.Flush()
webDriver.Back() // 回退到上级页面重复步骤抓取
}
所有源码如下,初学者,轻喷~~
// StartCrawler 开始爬取数据
func StartCrawler() {
log.Println("Start Crawling at ", time.Now().Format("2006-01-02 15:04:05"))
pageIndex := 0
for {
listContainer, err := webDriver.FindElement(selenium.ByXPATH, "//*[@id=\"viewlist_ul\"]")
if err != nil {
panic(err)
}
lists, err := listContainer.FindElements(selenium.ByClassName, "carinfo")
if err != nil {
panic(err)
}
log.Println("数据量:", len(lists))
pageIndex++
log.Printf("正在抓取第%d页数据...\n", pageIndex)
for i := 0; i < len(lists); i++ {
var urlElem selenium.WebElement
if pageIndex == 1 {
urlElem, err = webDriver.FindElement(selenium.ByXPATH, fmt.Sprintf("//*[@id='viewlist_ul']/li[%d]/a", i+13))
} else {
urlElem, err = webDriver.FindElement(selenium.ByXPATH, fmt.Sprintf("//*[@id='viewlist_ul']/li[%d]/a", i+1))
}
if err != nil {
break
}
url, err := urlElem.GetAttribute("href")
if err != nil {
break
}
webDriver.Get(url)
title, _ := webDriver.Title()
log.Printf("当前页面标题:%s\n", title)
modelElem, err := webDriver.FindElement(selenium.ByXPATH, "/html/body/div[5]/div[2]/div[1]/h2")
var model string
if err != nil {
log.Println(err)
model = "暂无"
} else {
model, _ = modelElem.Text()
}
log.Printf("model=[%s]\n", model)
priceElem, err := webDriver.FindElement(selenium.ByXPATH, "/html/body/div[5]/div[2]/div[2]/div/ins")
var price string
if err != nil {
log.Println(err)
price = "暂无"
} else {
price, _ = priceElem.Text()
price = fmt.Sprintf("%s万", price)
}
log.Printf("price=[%s]\n", price)
milesElem, err := webDriver.FindElement(selenium.ByXPATH, "/html/body/div[5]/div[2]/div[4]/ul/li[1]/span")
var miles string
if err != nil {
log.Println(err)
milesElem, err := webDriver.FindElement(selenium.ByXPATH, "/html/body/div[5]/div[2]/div[3]/ul/li[1]/span")
if err != nil {
log.Println(err)
miles = "暂无"
} else {
miles, _ = milesElem.Text()
}
} else {
miles, _ = milesElem.Text()
}
log.Printf("miles=[%s]\n", miles)
timeElem, err := webDriver.FindElement(selenium.ByXPATH, "/html/body/div[5]/div[2]/div[4]/ul/li[2]/span")
var date string
if err != nil {
log.Println(err)
timeElem, err := webDriver.FindElement(selenium.ByXPATH, "/html/body/div[5]/div[2]/div[3]/ul/li[2]/span")
if err != nil {
log.Println(err)
date = "暂无"
} else {
date, _ = timeElem.Text()
}
} else {
date, _ = timeElem.Text()
}
log.Printf("time=[%s]\n", date)
positionElem, err := webDriver.FindElement(selenium.ByXPATH, "/html/body/div[5]/div[2]/div[4]/ul/li[4]/span")
var position string
if err != nil {
log.Println(err)
positionElem, err := webDriver.FindElement(selenium.ByXPATH, "/html/body/div[5]/div[2]/div[3]/ul/li[4]/span")
if err != nil {
log.Println(err)
position = "暂无"
} else {
position, _ = positionElem.Text()
}
} else {
position, _ = positionElem.Text()
}
log.Printf("position=[%s]\n", position)
storeElem, err := webDriver.FindElement(selenium.ByXPATH, "/html/body/div[5]/div[2]/div[1]/div/div/div")
var store string
if err != nil {
log.Println(err)
store = "暂无"
} else {
store, _ = storeElem.Text()
store = strings.Replace(store, "商家|", "", -1)
if strings.Contains(store, "金牌店铺") {
store = strings.Replace(store, "金牌店铺", "", -1)
}
}
log.Printf("store=[%s]\n", store)
writer.Write([]string{model, miles, date, price, position, store})
writer.Flush()
webDriver.Back()
}
log.Printf("第%d页数据已经抓取完毕,开始下一页...\n", pageIndex)
nextButton, err := webDriver.FindElement(selenium.ByClassName, "page-item-next")
if err != nil {
log.Println("所有数据抓取完毕!")
break
}
nextButton.Click()
}
log.Println("Crawling Finished at ", time.Now().Format("2006-01-02 15:04:05"))
sendResult(dateTime)
}
整个代码如下,比较简单,不再赘述
func sendResult(fileName string) {
email := gomail.NewMessage()
email.SetAddressHeader("From", "re**ng@163.com", "张**")
email.SetHeader("To", email.FormatAddress("li**yang@163.com", "李**"))
email.SetHeader("Cc", email.FormatAddress("zhang**tao@163.net", "张**"))
email.SetHeader("Subject", "二手车之家-北京-二手车信息")
email.SetBody("text/plain;charset=UTF-8", "本周抓取到的二手车信息数据,请注意查收!\n")
email.Attach(fmt.Sprintf("data/%s.csv", fileName))
dialer := &gomail.Dialer{
Host: "smtp.163.com",
Port: 25,
Username: ${your_email}, // 替换自己的邮箱地址
Password: ${smtp_password}, // 自定义smtp服务器密码
SSL: false,
}
if err := dialer.DialAndSend(email); err != nil {
log.Println("邮件发送失败!err: ", err)
return
}
log.Println("邮件发送成功!")
}
defer service.Stop() // 停止chromedriver
defer webDriver.Quit() // 关闭浏览器
defer csvFile.Close() // 关闭文件流
总结