Используйте chromedp для решения проблем с антисканированием

задняя часть рептилия Chrome

предисловие

В последнее время давно не обновлялись новости об объявлении Управления по академическим вопросам Вечуаньского университета.Я думал, что IP мог быть заблокирован.Я проверил журнал и обнаружил, что это не так.Вместо этого полученные страницы были все заменены на обфусцированные js.две форматированные функции

function _$Es(_$Cu) {
    _$Cu[14] = _$v9();
    _$Cu[_$yf(_$ox(), 16)] = _$Dn();
    var _$cR = _$CR();
    _$cR = _$iT();
    return _$DA();
}

function _$Dk(_$Cu) {
    var _$x5 = _$Dv();
    var _$x5 = _$EB();
    if (_$Ex()) {
        _$w9 = _$Dw();
    }
    _$Cu[_$yf(_$EJ(), 16)] = _$ED();
    _$Cu[_$yf(_$Ep(), 16)] = _$EP();
    _$w9 = _$EB();
    return _$Cu[_$yf(_$v9(), 16)];
}

function _$rK() {
    var _$aJ = _$c0(_$DN());
    _$aJ = _$BC(_$aJ, 2);
    var _$Ce = _$yr(_$qt());
    for (var _$Cu = 0; _$Cu < _$aJ[_$gX()]; _$Cu++) {
        _$aJ[_$Cu] = _$Ce + _$aJ[_$Cu];
    }
    return _$aJ;
}

Смотреть на эту кучу — большая голова, но в соответствии с принципом, что до тех пор, пока браузер может отображать, сканер страниц может сканировать, мы будем решать эту проблему шаг за шагом.

анализировать

  1. Сначала я использовал postman, чтобы отправить запрос, и обнаружил, что была возвращена куча искаженных символов.
  2. Скопируйте заголовок запроса обычной страницы рендеринга и повторно отправьте запрос, чтобы получить обычную страницу. Рассмотрим две возможности: одна — это специальная обработка заголовка, а другая — проблема с файлом cookie.
  3. Остальное содержимое заголовка остается без изменений, удалите куки и повторно отправьте запрос, и снова получите кучу искаженных символов. Проблема успешно обнаружена, это должна быть проблема с файлами cookie
  4. Очистить кеш хрома, перезагрузить страницу, проверить запись запроса, видно, что страница загружается дважды всего第一次加载Куки не возвращаются при первой загрузке第二次加载Вторая загрузка вернулаJSESSIONID, это должен быть последний необходимый файл cookie
  5. Наблюдая за серединой двух запросов, мы можем обнаружить, что есть еще два запроса.Эти два запроса должны быть причиной возврата куки во второй раз.Первый запрос - это js файл внешней ссылки на странице, а второй запрос должен быть Запрос, отправленный запутанным js, ушел.
  6. Из-за ограниченной силы я не анализировал, как загружается эта логика после нескольких часов анализа. Но я подумал о том, чтобы скопировать файл cookie прямо из браузера и использовать его для сканера? Но с этим тоже есть проблема, то есть невозможно каждый раз получать куку вручную, что не даст нужного эффекта. Затем посмотрите, что Python используетSeleniumЧтобы полностью имитировать рендеринг в браузере, а затем проанализировать случай с поисковым роботом, я узнал, есть ли подобное решение для рендеринга в браузере в golang, и нашел его на всемогущем гейхабе.chromedp. Следующее использует chromedp для решения этой проблемы.

chromedp

Package chromedp is a faster, simpler way to drive browsers (Chrome, Edge, Safari, Android, etc) without external dependencies (ie, Selenium, PhantomJS, etc) using the Chrome Debugging Protocol.

1.установить (рекомендуется использовать лестницу)
go get -u github.com/chromedp/chromedp
2.code

Запустив следующий код, вы увидите, что хром откроет всплывающее окно и запустит веб-страницу и, наконец, выведет ожидаемый HTML-код в консоль, но нам нужно только получить правильный файл cookie, который позже используется для сканирования веб-страницы. . Если всем страницам нужно ждать, пока хром просканирует после рендеринга, то эффективность слишком низкая.

package main

import (
	"context"
	"fmt"
	"io/ioutil"
	"log"
	"time"

	"github.com/chromedp/cdproto/cdp"
	"github.com/chromedp/chromedp"
)

func main() {
	var err error

	// create context
	ctxt, cancel := context.WithCancel(context.Background())
	defer cancel()

	// create chrome instance
	c, err := chromedp.New(ctxt, chromedp.WithLog(log.Printf))
	if err != nil {
		log.Fatal(err)
	}

	// run task list
	var res string
	err = c.Run(ctxt, chromedp.Tasks{
        // 访问教务处页面
		chromedp.Navigate(`http://jwc.scu.edu.cn/jwc/moreNotice.action`),
        // 等待table渲染成功,成功则说明已经获取到了正确的页面
        chromedp.WaitVisible(`table`, chromedp.ByQuery),
        // 获取body标签的html字符
		chromedp.OuterHTML("body", &res),
	})
	if err != nil {
		log.Fatal(err)
	}

	// 关闭chrome实例
	err = c.Shutdown(ctxt)
	if err != nil {
		log.Fatal(err)
	}

	// 等待chrome实例关闭
	err = c.Wait()
	if err != nil {
		log.Fatal(err)
	}

    // 输出html字符串
	log.Printf(res)
}
3. Получить куки

Измените код списка задач на шаге 2, чтобы получить файл cookie.После модификации вы можете увидеть, что в консоли выводится строка файла cookie.Используя этот файл cookie для проверки в почтальоне, вы можете обнаружить, что вы можете получить правильную страницу . На этом этапе он должен быть в основном завершен, но все еще есть недостаток: окно Chrome будет всплывать каждый раз, когда он запускается, сканер работает на сервере без страницы графического интерфейса и затраты времени на открытие экземпляра Chrome каждый раз. также больше.

// 将chromedp.OuterHTML("body", &res) 替换为下面的代码
chromedp.ActionFunc(func(ctx context.Context, h cdp.Executor) error {
    // 获取cookie
    cookies, err := network.GetAllCookies().Do(ctx, h)

    // 将cookie拼接成header请求中cookie字段的模式
    var c string
    for _, v := range cookies {
        c = c + v.Name + "=" + v.Value + ";"
    }
    log.Println(c)

    if err != nil {
        return err
    }
    return nil
}),
5. Используйте безголовый режим Chrome

а. Используйте докер для запуска безголового режима Chrome

docker run -d -p 9222:9222 --rm --name chrome-headless knqz/chrome-headless

б. Изменить код

Вы можете видеть, что основное отличие состоит в том, что при создании экземпляра хрома вы не запускаете хром, и, конечно, вам не нужно его закрывать в конце.

package main

import (
	"context"
	"log"

	"github.com/chromedp/chromedp/client"

	"github.com/chromedp/cdproto/network"

	"github.com/chromedp/cdproto/cdp"

	"github.com/chromedp/chromedp"
)

func main() {
	var err error

	// create context
	ctxt, cancel := context.WithCancel(context.Background())
	defer cancel()

	// create chrome instance
	c, err := chromedp.New(ctxt, chromedp.WithTargets(client.New().WatchPageTargets(ctxt)), chromedp.WithLog(log.Printf))
	if err != nil {
		log.Fatal(err)
	}

	// run task list
	err = c.Run(ctxt, chromedp.Tasks{
        // 访问教务处页面
		chromedp.Navigate(`http://jwc.scu.edu.cn/jwc/moreNotice.action`),
        // 等待table渲染成功,成功则说明已经获取到了正确的页面
        chromedp.WaitVisible(`table`, chromedp.ByQuery),
        // 获取body标签的html字符
		chromedp.ActionFunc(func(ctx context.Context, h cdp.Executor) error {
            // 获取cookie
            cookies, err := network.GetAllCookies().Do(ctx, h)

            // 将cookie拼接成header请求中cookie字段的模式
            var c string
            for _, v := range cookies {
                c = c + v.Name + "=" + v.Value + ";"
            }
            log.Println(c)

            if err != nil {
                return err
            }
            return nil
        }),
	})
	if err != nil {
		log.Fatal(err)
	}
}

Вы можете в основном использовать его здесь.После получения файла cookie вы можете использовать метод, который вам нравится, чтобы получить страницу.