Впервые в статье о воде Наггетс я был немного взволнован, ха-ха
На этот раз мы используем Golang, чтобы получить знаменитые (la ji) игровые медиа.Кочевая звезда
Основные используемые сторонние пакеты:goquery, для разбора HTML не имеет значения, если вы не использовали goquery, это очень просто.
Второй — вставить данные в MySql с помощью Golang.
Во-первых, используйтеnet/http
страница запроса пакета
func main() {
url := "https://www.gamersky.com/news/"
resp, err := http.Get(url)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
if res.StatusCode != 200 {
log.Fatalf("status code error: %d %s", res.StatusCode, res.Status)
}
}
Здесь ошибки страницы запроса не допускаются, поэтому используйтеlog.Fatal
Выход сразу при возникновении ошибки.
Следующее использованиеgoquery.NewDocumentFromReader
Загрузите HTML как анализируемый тип.
// NewDocumentFromReader returns a Document from an io.Reader.
html, err := goquery.NewDocumentFromReader(resp.Body)
Далее мы можем использоватьgoquery
Разобрать HTML-страницу.
Сначала мы получаем все ссылки на новости на этой странице
Здесь ссылка на новости появляется по адресуclass="tt"
изa
вкладка, поэтому мы используемgoquery
, проанализируйте атрибут href всех тегов a с атрибутом «tt» под страницей, и тогда вы сможете получить все ссылки на новости под измененной страницей.
func getNewsList(html *goquery.Document, newsList []string) []string {
html.Find("a[class=tt]").Each(func(i int, selection *goquery.Selection) {
url, _ := selection.Attr("href")
newsList = append(newsList, url)
})
return newsList
}
Таким образом, мы получаем все ссылки на новости с главной страницы новостей и помещаем все ссылки вnewsList
в этом отрезке.
Далее, давайте начнем сканировать конкретные новости в этих новостных ссылках.
Используйте горутину для реализации одновременных запросов этих новостных ссылок и анализа результатов.
var newsList []string
newsList = getNewsList(html, newsList)
var wg sync.WaitGroup
for i := 0; i < len(newsList); i++ {
wg.Add(1)
go getNews(newsList[i], &wg)
}
wg.Wait()
Сначала мы инициализируемsync.WaitGroup
, который используется для управления выполнением горутин и гарантирует, что все горутины выполняются до конца.
Пройдите через это, где мы храним все ссылки на новостиnewsList
, новостная ссылка запускает соответствующую горутину для обработки следующей обработки.
wg.Wait()
Используется для блокировки выполнения программы до тех пор, пока не будут выполнены все задачи в wg.
Затем начните анализировать каждую страницу новостей, чтобы получить нужные нам данные.
Сначала мы определяемNews
эта структура.
type News struct {
Title string
Media string
Url string
PubTime string
Content string
}
Как и в первом шаге, сначала нам нужно запросить ссылку на новость.
func getNews(url string, wg *sync.WaitGroup) {
resp, err := http.Get(url)
if err != nil {
log.Println(err)
wg.Done()
return
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
log.Printf("Error: status code %d", resp.StatusCode)
wg.Done()
return
}
html, err := goquery.NewDocumentFromReader(resp.Body)
news := News{}
Благодаря вышеописанным шагам HTML, который мы успешно запросили, был преобразован в объект, который можно проанализировать с помощью goquer.
Заголовок находится в h1 под div с class="Mid2L_tit".
html.Find("div[class=Mid2L_tit]>h1").Each(func(i int, selection *goquery.Selection) {
news.Title = selection.Text()
})
if news.Title == "" {
wg.Done()
return
}
Формат отдельных столбцов новостей здесь отличается от формата обычных страниц новостей, поэтому пока с ним можно хорошо обращаться, поэтому, когда заголовок не будет проанализирован, он будет возвращен.
Далее идет обработка времени, мы видим, что времяdiv class="detail"
Однако время, проанализированное таким образом, не может быть напрямую сохранено в базе данных.Здесь я использую регулярные выражения для извлечения всех дат и времени, а затем объединяю их в формат, который можно сохранить в базе данных.
var tmpTime string
html.Find("div[class=detail]").Each(func(i int, selection *goquery.Selection) {
tmpTime = selection.Text()
})
reg := regexp.MustCompile(`\d+`)
timeString := reg.FindAllString(tmpTime, -1)
news.PubTime = fmt.Sprintf("%s-%s-%s %s:%s:%s", timeString[0], timeString[1], timeString[2], timeString[3], timeString[4], timeString[5])
Если есть лучший способ, ты должен научить меня! ! !
Далее следует парсить текст новости
Текст новости есть весьdiv class="Mid2L_con"
в теге p ниже.
html.Find("div[class=Mid2L_con]>p").Each(func(i int, selection *goquery.Selection) {
news.Content = news.Content + selection.Text()
})
Теперь, когда у нас есть все необходимые данные, следующим шагом будет сохранение этих данных в MySql.
Сначала создайте таблицу с именем gamesky.
create table gamesky
(
id int auto_increment
primary key,
title varchar(256) not null,
media varchar(16) not null,
url varchar(256) not null,
content varchar(4096) null,
pub_time timestamp default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP,
create_time timestamp default CURRENT_TIMESTAMP not null
);
Далее мы устанавливаем соединение Mysql.
package mysql
import (
"database/sql"
"fmt"
"os"
_ "github.com/go-sql-driver/mysql"
)
var db *sql.DB
func init() {
db, _ = sql.Open("mysql", "root:root@tcp(127.0.0.1:3306)/game_news?charset=utf8")
db.SetMaxOpenConns(1000)
err := db.Ping()
if err != nil {
fmt.Println("Failed to connect to mysql, err:" + err.Error())
os.Exit(1)
}
}
func DBCon() *sql.DB {
return db
}
Следующим шагом является использование установленного нами соединения MySql для сохранения полученных данных.
db := mysql.DBCon()
stmt, err := db.Prepare(
"insert into news (`title`, `url`, `media`, `content`, `pub_time`) values (?,?,?,?,?)")
if err != nil {
log.Println(err)
wg.Done()
}
defer stmt.Close()
rs, err := stmt.Exec(news.Title, news.Url, news.Media, news.Content, news.PubTime)
if err != nil {
log.Println(err)
wg.Done()
}
if id, _ := rs.LastInsertId(); id > 0 {
log.Println("插入成功")
}
wg.Done()
rs.LastInsertId()
Он используется для получения идентификатора данных, только что вставленных в базу данных.Если вставка прошла успешно, будет возвращен идентификатор соответствующей записи, чтобы мы могли узнать, была ли вставка успешной.
Длина текста новости иногда превышает длину столбца, установленного в MySql, вы можете изменить длину столбца или перехватить часть текста для сохранения.
После возникновения ошибки в горутине или после сохранения базы данных не забудьтеwg.Done()
чтобы уменьшить количество задач в wg на 1.
Таким образом, наш сканер будет одновременно захватывать новости и сохранять их в базе данных.
Видно, что из-за того, что наша скорость сканирования слишком высока, сработал антикраулер Nomad Star, поэтому нам нужно уменьшить частоту, но это потеряет преимущество параллелизма Golang, поэтому мы хотим захватывать данные одновременно и Не хочу, чтобы мне противостояли.Для краулеров необходимо настроить хороший пул прокси, но я не буду это здесь объяснять.
Далее переходим к полному коду сканераpackage main
import (
"fmt"
"game_news/mysql"
"log"
"net/http"
"regexp"
"sync"
"github.com/PuerkitoBio/goquery"
)
type News struct {
Title string
Media string
Url string
PubTime string
Content string
}
func main() {
url := "https://www.gamersky.com/news/"
resp, err := http.Get(url)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
log.Fatalf("status code error: %d %s", resp.StatusCode, resp.Status)
}
html, err := goquery.NewDocumentFromReader(resp.Body)
var newsList []string
newsList = getNewsList(html, newsList)
var wg sync.WaitGroup
for i := 0; i < len(newsList); i++ {
wg.Add(1)
go getNews(newsList[i], &wg)
}
wg.Wait()
}
func getNewsList(html *goquery.Document, newsList []string) []string {
// '//a[@class="tt"]/@href'
html.Find("a[class=tt]").Each(func(i int, selection *goquery.Selection) {
url, _ := selection.Attr("href")
newsList = append(newsList, url)
})
return newsList
}
func getNews(url string, wg *sync.WaitGroup) {
resp, err := http.Get(url)
if err != nil {
log.Println(err)
wg.Done()
return
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
log.Printf("Error: status code %d", resp.StatusCode)
wg.Done()
return
}
html, err := goquery.NewDocumentFromReader(resp.Body)
news := News{}
news.Url = url
news.Media = "GameSky"
html.Find("div[class=Mid2L_tit]>h1").Each(func(i int, selection *goquery.Selection) {
news.Title = selection.Text()
})
if news.Title == "" {
wg.Done()
return
}
html.Find("div[class=Mid2L_con]>p").Each(func(i int, selection *goquery.Selection) {
news.Content = news.Content + selection.Text()
})
var tmpTime string
html.Find("div[class=detail]").Each(func(i int, selection *goquery.Selection) {
tmpTime = selection.Text()
})
reg := regexp.MustCompile(`\d+`)
timeString := reg.FindAllString(tmpTime, -1)
news.PubTime = fmt.Sprintf("%s-%s-%s %s:%s:%s", timeString[0], timeString[1], timeString[2], timeString[3], timeString[4], timeString[5])
db := mysql.DBCon()
stmt, err := db.Prepare(
"insert into gamesky (`title`, `url`, `media`, `content`, `pub_time`) values (?,?,?,?,?)")
if err != nil {
log.Println(err)
wg.Done()
}
defer stmt.Close()
rs, err := stmt.Exec(news.Title, news.Url, news.Media, news.Content, news.PubTime)
if err != nil {
log.Println(err)
wg.Done()
}
if id, _ := rs.LastInsertId(); id > 0 {
log.Println("插入成功")
}
wg.Done()
}
На этом статья окончена.Если у вас есть какие-либо вопросы по вышеуказанным статьям, просветите меня, спасибо большое! ! !