Блог

Как узнать кодировку файла в Ubuntu?

Можно с помощью программы enca.

Устанавливаем:

$ sudo apt install -y enca

Запускаем:

$ enca myfile.txt

Видим результат, например, такой:

Universal transformation format 8 bits; UTF-8
  Doubly-encoded to UTF-8 from ISO-8859-5
bash

ERROR: could not access file “$libdir/postgis-2.4”: No such file or directory

Встретил такую ошибку. Использовал образ https://hub.docker.com/r/mdillon/postgis

Выяснилось, что образ обновил postgis до версии 2.5 (2.4 был полностью убран).

Чтобы все починить, просто выполните команду внутри контейнера /usr/local/bin/update-postgis.sh - это обновит postgis с версии 2.4 до версии 2.5 внутри вашего контейнера. Возможно, придется перезагрузить его, но у меня все заработало.

devops docker postgresql

Защита от ботов Distil Networks

Сегодня узнал о крутой защите от ботов. Блокирует не только традиционными способами, но и активно используя машинное обучение.

Называется она - Distil Networks

Если использовать вместе с рекапчей от гугла, то на сайт вообще практически невозможно пробиться автоматизированными путями. Да, селениум тоже заблокирует. Да, может не с первого раза, но на 2-3 заблокирует.

Кому очень надо, вот здесь ребята с реддит уверяют, что могут обойти защиту https://www.reddit.com/r/webdev/comments/5q1ypx/what_is_your_approach_on_scraping_distil_networks/

сайты

Простейший работающий пример docker + chromedriver + selenium + python на сервере

Скачать пример файлом - example_chromedriver.zip

Структура папок и файлов:

wrapper/Dockerfile
wrapper/requirements.txt
wrapper/main.py
docker-compose-yml

Содержимое docker-compose.yml:

version: "2"

services:

  web:
    build: wrapper
    restart: always
    command: python main.py

Содержимое wrapper/Dockerfile:

FROM python:3.6

ENV PYTHONUNBUFFERED 1

# install google chrome
RUN wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -
RUN sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google-chrome.list'
RUN apt-get -y update
RUN apt-get install -y google-chrome-stable

# install chromedriver
RUN apt-get install -yqq unzip
RUN wget -O /tmp/chromedriver.zip http://chromedriver.storage.googleapis.com/`curl -sS chromedriver.storage.googleapis.com/LATEST_RELEASE`/chromedriver_linux64.zip
RUN unzip /tmp/chromedriver.zip chromedriver -d /usr/local/bin/

# set display port to avoid crash
ENV DISPLAY=:99

RUN mkdir /code
WORKDIR /code

COPY . /code/

RUN pip install -r requirements.txt

Содержимое wrapper/requirements.txt:

selenium==3.8.0

Содержимое wrapper/main.py:

from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import time


chrome_options = webdriver.ChromeOptions()
chrome_options.add_argument('--no-sandbox')
chrome_options.add_argument('--headless')
chrome_options.add_argument('--disable-gpu')
driver = webdriver.Chrome(chrome_options=chrome_options)
driver.implicitly_wait(10)

while True:
    print('start')
    driver.get("http://www.python.org")
    assert "Python" in driver.title
    elem = driver.find_element_by_name("q")
    print('elem', elem)
    elem.clear()
    elem.send_keys("pycon")
    elem.send_keys(Keys.RETURN)
    assert "No results found." not in driver.page_source
    print('all ok')
    time.sleep(3)

driver.close()

Источник: https://github.com/joyzoursky/docker-python-chromedriver

python selenium

Табличный вид заказа в письме в битриксе версии 18

Вот таким образом можно сделать табличный вид заказа с торговыми предложениями и самими товарами в письме. Для вывода таблицы в шаблоне нового заказа необходимо использовать переменную #ORDER_TABLE_ITEMS#. Картинки тоже прилагаются.

Файл init.php:

<?
AddEventHandler("sale", "OnOrderNewSendEmail", "ModifyOrderSaleMails");

function ModifyOrderSaleMails($orderID, &$eventName, &$arFields) {
    if(CModule::IncludeModule("sale") && CModule::IncludeModule("iblock")) {
        $strOrderList = "";
        $dbBasketItems = CSaleBasket::GetList(
            array("NAME" => "ASC"),
            array("ORDER_ID" => $orderID),
            false,
            false,
            array("PRODUCT_ID", "ID", "NAME", "QUANTITY", "PRICE", "CURRENCY")
        );
        $strOrderList = "
                <style>
                    table.example {
                        border-collapse: collapse;
                        width: 585px;
                    }
                    table.example td {
                        border:1px solid black;
                    }
                    table.example td.num {
                        width: 30px;
                        text-align: center;
                    }
                </style>
                <table class=\"example\">
                    <tr>
                        <td class=\"num\" align=\"center\">№</td>
                        <td align=\"center\">Фото товара</td>
                        <td align=\"center\">Наименование</td>
                        <td align=\"center\">Количество</td>
                        <td align=\"center\">Цена, руб.</td>
                        <td align=\"center\">Cумма, руб.</td>
                    </tr>";
        $i = 0;
        while ($arProps = $dbBasketItems->Fetch()) {
            $i += 1;
            // image
            $offer = CIBlockElement::GetById(IntVal($arProps["PRODUCT_ID"]))->GetNext();
            $pictureId = $offer["PREVIEW_PICTURE"];
            if (is_null($pictureId)) {
                $realProductInfo = CCatalogSku::GetProductInfo(IntVal($arProps["PRODUCT_ID"]));
                $realProduct = CIBlockElement::GetById(IntVal($realProductInfo["ID"]))->GetNext();
                $pictureId = $realProduct["PREVIEW_PICTURE"];
            }
            $thumb = CFile::ResizeImageGet(
                $pictureId,
                array("width" => 200, "height" => 200),
                BX_RESIZE_IMAGE_PROPORTIONAL,
                false
            );
            $thumb = $thumb["src"];
            // sum
            $summ = $arProps['QUANTITY'] * $arProps['PRICE'];
            $strOrderList .= "<tr><td class=\"num\">".$i."</td><td><img src=\"http://".$_SERVER['SERVER_NAME'] . $thumb."\"></td><td>".$arProps['NAME']."</td><td>".$arProps['QUANTITY']."</td><td>".number_format((float)$arProps['PRICE'], 2, '.', '')."</td><td>".number_format((float)$summ, 2, '.', '')."</td><tr>";
        }
        echo "</table>";
        // пишем в переменную для письма
        $arFields["ORDER_TABLE_ITEMS"] = $strOrderList; 
    }
}
?>

Подобным образом можно добавить и другие переменные к письму.

bitrix php

Ansible Playbook для создания нового сервера на vscale.io

Написал плейбук для создания нового сервера для небольших сайтов на хостинге https://vscale.io.

Работает следующим образом:

  1. Запускаете, спрашивает название скейлета на хостинге.
  2. Спрашивает путь к репозиторию, клонирует его локально.
  3. Создает новый сервак на хостинге, заходит на него, устанавливает докер и его зависимости.
  4. Копирует туда указанную в vars.yml директорию.
  5. Выполняет команду docker-compose up -d на сервере.

Найти последнюю версию можно здесь https://github.com/crusat/ansible_vscale_new_server, также прикрепил здесь версию на момент создания статьи.

devops docker

Как установить модули python без интернета с помощью pip

Для начала надо скачать все модули, которые перечислены в requirements.txt. Проще всего через докер.

$ docker run -it --rm --name my -v "$PWD":/tmp/pip_cache python:3.6 pip download -r /tmp/pip_cache/requirements.txt -d /tmp/pip_cache/

Обратите внимание на версию. Тут используется 3.6 - она должна совпадать с той версией питона, который установлен на сервере. Иначе Pillow, psycopg2 и т.п. скомпилированные модули не поставятся.

Затем можно залить их и установить с помощью такой команды:

$ pip install -r requirements.txt -f /path/to/pip/cache/ --no-index

Все, после этого можно использовать.

copypaste python

Редирект nginx

HTTP

server {
    listen 80;
    server_name www.example.com;

    location /.well-known/acme-challenge/ {
        root /home/garpix/www/example.com/;
    }

    location / {
       return 301 https://$host$request_uri;
    }
}

HTTPS (letsencrypt)

server {
    listen 443 ssl;
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    server_name www.example.com;
    return 301 https://example.com$request_uri;
}
devops nginx

Буква Й - файл не найден

Если вы скопируете файл, содержащий в названии эту замечательную букву с Linux-машины на MacOS-машину и обратно, то
попытавшись обнаружить этот файл, вы, скорее всего, уже не сможете этого сделать (ответом будет File not found):

$ ls *й*

Учитывая, что при простом отображении файлов в директории, вы его увидите:

$ ls

Это происходит потому, что букву Й можно закодировать в UTF-8 как один или два символа. И эти две системы делают это
по-разному. При копировании на MacOS файл кодируется по-другому и при копировании обратно, он не будет уже тем старым
добрым Й, который вы знали на Linux-машине. Короче, аккуратнее с этими вещами.

Спасибо человеку, ответившему на тот же вопрос, с которым я столкнулся: http://ru.stackoverflow.com/a/481109

bash linux macos

Простой конфиг обратного прокси в nginx

Для проксирования запросов через nginx “куда-то еще” можно использовать подобный скрипт. Это называется “reverse proxy”.

В случае ниже, nginx слушает 80 порт для example.com и перенаправляет запрос на http://127.0.0.1:1000. При этом, он сохранит server_name и IP посетителя.

server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://127.0.0.1:1000;
        proxy_set_header Host $server_name;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_connect_timeout       60;
        proxy_send_timeout          60;
        proxy_read_timeout          60;
        send_timeout                60;
    }
}
copypaste